OsmAnd translator
This commit is contained in:
parent
e78b0bf915
commit
f5403dfc72
4 changed files with 456 additions and 24 deletions
|
@ -5,18 +5,13 @@ import java.io.File;
|
|||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.osmand.translator.utils.FieldsHandler;
|
||||
import net.osmand.translator.utils.MethodHandler;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IMethod;
|
||||
|
@ -25,29 +20,148 @@ import org.eclipse.jdt.core.IPackageFragmentRoot;
|
|||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.core.compiler.IProblem;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jface.text.Document;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.devtools.j2objc.J2ObjC;
|
||||
import com.google.devtools.j2objc.Options;
|
||||
import com.google.devtools.j2objc.gen.ObjectiveCHeaderGenerator;
|
||||
import com.google.devtools.j2objc.gen.ObjectiveCImplementationGenerator;
|
||||
import com.google.devtools.j2objc.sym.Symbols;
|
||||
import com.google.devtools.j2objc.types.Types;
|
||||
import com.google.devtools.j2objc.util.ASTNodeException;
|
||||
import com.google.devtools.j2objc.util.NameTable;
|
||||
|
||||
|
||||
public class TranslationHandler {
|
||||
|
||||
public static void execute(String inFile, PrintStream out) throws IOException {
|
||||
// parse "MapUtils.java"
|
||||
BufferedReader fr = new BufferedReader(new FileReader(new File(inFile)));
|
||||
String readLine;
|
||||
String buf = "";
|
||||
while ((readLine = fr.readLine()) != null) {
|
||||
buf += readLine;
|
||||
}
|
||||
fr.close();
|
||||
CompilationUnit parse = parse(buf);
|
||||
FieldsHandler.printFieldsInfo(parse, out);
|
||||
out.println();
|
||||
MethodHandler.printMethodsInfo(parse, out);
|
||||
// BufferedReader fr = new BufferedReader(new FileReader(new File(inFile)));
|
||||
// String readLine;
|
||||
// String buf = "";
|
||||
// while ((readLine = fr.readLine()) != null) {
|
||||
// buf += readLine;
|
||||
// }
|
||||
// fr.close();
|
||||
|
||||
String source = Files.toString(new File(inFile), Charset.defaultCharset());
|
||||
CompilationUnit unit = parse(inFile, source);
|
||||
|
||||
try {
|
||||
unit.recordModifications();
|
||||
NameTable.initialize(unit);
|
||||
Types.initialize(unit);
|
||||
Symbols.initialize(unit);
|
||||
String newSource = J2ObjC.translate(unit, source);
|
||||
if (unit.types().isEmpty()) {
|
||||
System.out.println("skipping dead file " + inFile);
|
||||
} else {
|
||||
if (Options.printConvertedSources()) {
|
||||
try {
|
||||
File outputFile = new File(Options.getOutputDirectory(), inFile);
|
||||
outputFile.getParentFile().mkdirs();
|
||||
Files.write(newSource, outputFile, Charset.defaultCharset());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("writing output file(s) to " + Options.getOutputDirectory().getAbsolutePath());
|
||||
ObjectiveCHeaderGenerator.generate(inFile, source, unit);
|
||||
ObjectiveCImplementationGenerator.generate(inFile, Options.getLanguage(), unit, source);
|
||||
}
|
||||
} catch (ASTNodeException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
NameTable.cleanup();
|
||||
Symbols.cleanup();
|
||||
Types.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private static CompilationUnit parse(String filename, String source) {
|
||||
System.out.println("parsing " + filename);
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS3);
|
||||
Map<String, String> compilerOptions = Options.getCompilerOptions();
|
||||
parser.setCompilerOptions(compilerOptions);
|
||||
parser.setSource(source.toCharArray());
|
||||
parser.setResolveBindings(true);
|
||||
setPaths(parser);
|
||||
parser.setUnitName(filename);
|
||||
CompilationUnit unit = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
for (IProblem problem : getCompilationErrors(unit)) {
|
||||
if (problem.isError()) {
|
||||
System.out.println(String.format("%s:%s: %s",
|
||||
filename, problem.getSourceLineNumber(), problem.getMessage()));
|
||||
}
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
|
||||
private static List<IProblem> getCompilationErrors(CompilationUnit unit) {
|
||||
List<IProblem> errors = Lists.newArrayList();
|
||||
for (IProblem problem : unit.getProblems()) {
|
||||
if (problem.isError()) {
|
||||
if (((problem.getID() & IProblem.ImportRelated) > 0) && Options.ignoreMissingImports()) {
|
||||
continue;
|
||||
} else {
|
||||
errors.add(problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static void setPaths(ASTParser parser) {
|
||||
// Add existing boot classpath after declared path, so that core files
|
||||
// referenced, but not being translated, are included. This only matters
|
||||
// when compiling the JRE emulation library sources.
|
||||
List<String> fullClasspath = Lists.newArrayList();
|
||||
String[] classpathEntries = Options.getClassPathEntries();
|
||||
for (int i = 0; i < classpathEntries.length; i++) {
|
||||
fullClasspath.add(classpathEntries[i]);
|
||||
}
|
||||
String bootclasspath = Options.getBootClasspath();
|
||||
for (String path : bootclasspath.split(":")) {
|
||||
// JDT requires that all path elements exist and can hold class files.
|
||||
File f = new File(path);
|
||||
if (f.exists() && (f.isDirectory() || path.endsWith(".jar"))) {
|
||||
fullClasspath.add(path);
|
||||
}
|
||||
}
|
||||
parser.setEnvironment(fullClasspath.toArray(new String[0]), Options.getSourcePathEntries(),
|
||||
null, true);
|
||||
|
||||
// Workaround for ASTParser.setEnvironment() bug, which ignores its
|
||||
// last parameter. This has been fixed in the Eclipse post-3.7 Java7
|
||||
// branch.
|
||||
try {
|
||||
Field field = parser.getClass().getDeclaredField("bits");
|
||||
field.setAccessible(true);
|
||||
int bits = field.getInt(parser);
|
||||
// Turn off CompilationUnitResolver.INCLUDE_RUNNING_VM_BOOTCLASSPATH
|
||||
bits &= ~0x20;
|
||||
field.setInt(parser, bits);
|
||||
} catch (Exception e) {
|
||||
// should never happen, since only the one known class is manipulated
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
////////////////////// OLD ////////////////////////////
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private static CompilationUnit parse(String source) {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS3);
|
||||
parser.setResolveBindings(true);
|
||||
|
@ -62,7 +176,6 @@ public class TranslationHandler {
|
|||
return unit;
|
||||
}
|
||||
|
||||
|
||||
private void printProjectInfo(IProject project) throws CoreException, JavaModelException {
|
||||
System.out.println("Working in project " + project.getName());
|
||||
// Check if we have a Java project
|
||||
|
@ -120,5 +233,4 @@ public class TranslationHandler {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
package net.osmand.translator.utils;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
|
||||
import net.osmand.translator.visitor.MethodVisitor;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Block;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Type;
|
||||
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
|
||||
|
||||
import com.google.devtools.j2objc.gen.ObjectiveCHeaderGenerator;
|
||||
|
||||
public class MethodHandler extends AbstractHandler{
|
||||
|
||||
|
@ -38,6 +45,12 @@ public class MethodHandler extends AbstractHandler{
|
|||
|
||||
|
||||
private static void applyParameters(MethodDeclaration method, StringBuffer buffer) {
|
||||
List parameters = method.parameters();
|
||||
for (Object p : parameters) {
|
||||
ASTNode node = (ASTNode)p;
|
||||
buffer.append(node.toString() + ", ");
|
||||
}
|
||||
buffer.deleteCharAt(buffer.length()-2);
|
||||
}
|
||||
|
||||
private static void fillBody(MethodDeclaration method, StringBuffer buffer) {
|
||||
|
@ -46,8 +59,10 @@ public class MethodHandler extends AbstractHandler{
|
|||
buffer.append(body);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void parseSimpleForStatement() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
|
||||
/**
|
||||
* This utility class includes :
|
||||
* 1. distance algorithms
|
||||
* 2. finding center for array of nodes
|
||||
* 3. tile evaluation algorithms
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class MapUtils_1 {
|
||||
|
||||
private static final String BASE_SHORT_OSM_URL = "http://osm.org/go/";
|
||||
|
||||
/**
|
||||
* This array is a lookup table that translates 6-bit positive integger
|
||||
* index values into their "Base64 Alphabet" equivalents as specified
|
||||
* in Table 1 of RFC 2045.
|
||||
*/
|
||||
private static final char intToBase64[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '@'
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets distance in meters
|
||||
*/
|
||||
public static double getDistance(double lat1, double lon1, double lat2, double lon2){
|
||||
double R = 6371; // km
|
||||
double dLat = Math.toRadians(lat2-lat1);
|
||||
double dLon = Math.toRadians(lon2-lon1);
|
||||
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
||||
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
|
||||
Math.sin(dLon/2) * Math.sin(dLon/2);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
return R * c * 1000;
|
||||
}
|
||||
|
||||
public static double checkLongitude(double longitude) {
|
||||
while (longitude < -180 || longitude > 180) {
|
||||
if (longitude < 0) {
|
||||
longitude += 360;
|
||||
} else {
|
||||
longitude -= 360;
|
||||
}
|
||||
}
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public static double checkLatitude(double latitude) {
|
||||
while (latitude < -90 || latitude > 90) {
|
||||
if (latitude < 0) {
|
||||
latitude += 180;
|
||||
} else {
|
||||
latitude -= 180;
|
||||
}
|
||||
}
|
||||
if(latitude < -85.0511) {
|
||||
return -85.0511;
|
||||
} else if(latitude > 85.0511){
|
||||
return 85.0511;
|
||||
}
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public static int get31TileNumberX(double longitude){
|
||||
longitude = checkLongitude(longitude);
|
||||
long l = 1l << 31;
|
||||
return (int)((longitude + 180d)/360d * l);
|
||||
}
|
||||
public static int get31TileNumberY( double latitude){
|
||||
latitude = checkLatitude(latitude);
|
||||
double eval = Math.log( Math.tan(Math.toRadians(latitude)) + 1/Math.cos(Math.toRadians(latitude)) );
|
||||
long l = 1l << 31;
|
||||
if(eval > Math.PI){
|
||||
eval = Math.PI;
|
||||
}
|
||||
return (int) ((1 - eval / Math.PI) / 2 * l);
|
||||
}
|
||||
|
||||
public static double get31LongitudeX(int tileX){
|
||||
return getLongitudeFromTile(21, tileX /1024f);
|
||||
}
|
||||
|
||||
public static double get31LatitudeY(int tileY){
|
||||
return getLatitudeFromTile(21, tileY /1024f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Theses methods operate with degrees (evaluating tiles & vice versa)
|
||||
* degree longitude measurements (-180, 180) [27.56 Minsk]
|
||||
// degree latitude measurements (90, -90) [53.9]
|
||||
*/
|
||||
|
||||
public static double getTileNumberX(float zoom, double longitude){
|
||||
if(longitude == 180d) {
|
||||
return getPowZoom(zoom) - 1;
|
||||
}
|
||||
longitude = checkLongitude(longitude);
|
||||
return (longitude + 180d)/360d * getPowZoom(zoom);
|
||||
}
|
||||
|
||||
public static double getTileNumberY(float zoom, double latitude){
|
||||
latitude = checkLatitude(latitude);
|
||||
double eval = Math.log( Math.tan(Math.toRadians(latitude)) + 1/Math.cos(Math.toRadians(latitude)) );
|
||||
if (Double.isInfinite(eval) || Double.isNaN(eval)) {
|
||||
latitude = latitude < 0 ? - 89.9 : 89.9;
|
||||
eval = Math.log( Math.tan(Math.toRadians(latitude)) + 1/Math.cos(Math.toRadians(latitude)) );
|
||||
}
|
||||
double result = (1 - eval / Math.PI) / 2 * getPowZoom(zoom);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double getTileEllipsoidNumberY(float zoom, double latitude){
|
||||
final double E2 = (double) latitude * Math.PI / 180;
|
||||
final long sradiusa = 6378137;
|
||||
final long sradiusb = 6356752;
|
||||
final double J2 = (double) Math.sqrt(sradiusa * sradiusa - sradiusb * sradiusb) / sradiusa;
|
||||
final double M2 = (double) Math.log((1 + Math.sin(E2))
|
||||
/ (1 - Math.sin(E2)))/ 2- J2 * Math.log((1 + J2 * Math.sin(E2))/ (1 - J2 * Math.sin(E2))) / 2;
|
||||
final double B2 = getPowZoom(zoom);
|
||||
return B2 / 2 - M2 * B2 / 2 / Math.PI;
|
||||
}
|
||||
|
||||
public static double getLatitudeFromEllipsoidTileY(float zoom, float tileNumberY){
|
||||
final double MerkElipsK = 0.0000001;
|
||||
final long sradiusa = 6378137;
|
||||
final long sradiusb = 6356752;
|
||||
final double FExct = (double) Math.sqrt(sradiusa * sradiusa
|
||||
- sradiusb * sradiusb)
|
||||
/ sradiusa;
|
||||
final double TilesAtZoom = getPowZoom(zoom);
|
||||
double result = (tileNumberY - TilesAtZoom / 2)
|
||||
/ -(TilesAtZoom / (2 * Math.PI));
|
||||
result = (2 * Math.atan(Math.exp(result)) - Math.PI / 2) * 180
|
||||
/ Math.PI;
|
||||
double Zu = result / (180 / Math.PI);
|
||||
double yy = (tileNumberY - TilesAtZoom / 2);
|
||||
|
||||
double Zum1 = Zu;
|
||||
Zu = Math.asin(1 - ((1 + Math.sin(Zum1)) * Math.pow(1 - FExct * Math.sin(Zum1), FExct))
|
||||
/ (Math.exp((2 * yy) / -(TilesAtZoom / (2 * Math.PI))) * Math.pow(1 + FExct * Math.sin(Zum1), FExct)));
|
||||
while (Math.abs(Zum1 - Zu) >= MerkElipsK) {
|
||||
Zum1 = Zu;
|
||||
Zu = Math.asin(1 - ((1 + Math.sin(Zum1)) * Math.pow(1 - FExct * Math.sin(Zum1), FExct))
|
||||
/ (Math.exp((2 * yy) / -(TilesAtZoom / (2 * Math.PI))) * Math.pow(1 + FExct * Math.sin(Zum1), FExct)));
|
||||
}
|
||||
|
||||
return Zu * 180 / Math.PI;
|
||||
}
|
||||
|
||||
|
||||
public static double getLongitudeFromTile(float zoom, double x) {
|
||||
return x / getPowZoom(zoom) * 360.0 - 180.0;
|
||||
}
|
||||
|
||||
public static double getPowZoom(float zoom){
|
||||
if(zoom >= 0 && zoom - Math.floor(zoom) < 0.05f){
|
||||
return 1 << ((int)zoom);
|
||||
} else {
|
||||
return Math.pow(2, zoom);
|
||||
}
|
||||
}
|
||||
|
||||
public static float calcDiffPixelX(float rotateSin, float rotateCos, float dTileX, float dTileY, float tileSize){
|
||||
return (rotateCos * dTileX - rotateSin * dTileY) * tileSize ;
|
||||
}
|
||||
|
||||
public static float calcDiffPixelY(float rotateSin, float rotateCos, float dTileX, float dTileY, float tileSize){
|
||||
return (rotateSin * dTileX + rotateCos * dTileY) * tileSize ;
|
||||
}
|
||||
|
||||
public static double getLatitudeFromTile(float zoom, double y){
|
||||
int sign = y < 0 ? -1 : 1;
|
||||
double result = Math.atan(sign*Math.sinh(Math.PI * (1 - 2 * y / getPowZoom(zoom)))) * 180d / Math.PI;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static int getPixelShiftX(int zoom, double long1, double long2, int tileSize){
|
||||
return (int) ((getTileNumberX(zoom, long1) - getTileNumberX(zoom, long2)) * tileSize);
|
||||
}
|
||||
|
||||
|
||||
public static int getPixelShiftY(int zoom, double lat1, double lat2, int tileSize){
|
||||
return (int) ((getTileNumberY(zoom, lat1) - getTileNumberY(zoom, lat2)) * tileSize);
|
||||
}
|
||||
|
||||
// Examples
|
||||
// System.out.println(buildShortOsmUrl(51.51829d, 0.07347d, 16)); // http://osm.org/go/0EEQsyfu
|
||||
// System.out.println(buildShortOsmUrl(52.30103d, 4.862927d, 18)); // http://osm.org/go/0E4_JiVhs
|
||||
// System.out.println(buildShortOsmUrl(40.59d, -115.213d, 9)); // http://osm.org/go/TelHTB--
|
||||
public static String buildShortOsmUrl(double latitude, double longitude, int zoom){
|
||||
long lat = (long) (((latitude + 90d)/180d)*(1l << 32));
|
||||
long lon = (long) (((longitude + 180d)/360d)*(1l << 32));
|
||||
long code = interleaveBits(lon, lat);
|
||||
StringBuilder str = new StringBuilder(10);
|
||||
str.append(BASE_SHORT_OSM_URL);
|
||||
// add eight to the zoom level, which approximates an accuracy of one pixel in a tile.
|
||||
for(int i=0; i< Math.ceil((zoom+8)/3d); i++){
|
||||
str.append(intToBase64[(int) ((code >> (58 - 6 * i)) & 0x3f)]);
|
||||
}
|
||||
// append characters onto the end of the string to represent
|
||||
// partial zoom levels (characters themselves have a granularity of 3 zoom levels).
|
||||
for(int j=0; j< (zoom + 8) % 3 ; j++){
|
||||
str.append('-');
|
||||
}
|
||||
str.append("?m");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* interleaves the bits of two 32-bit numbers. the result is known as a Morton code.
|
||||
*/
|
||||
private static long interleaveBits(long x, long y){
|
||||
long c = 0;
|
||||
for(byte b = 31; b>=0; b--){
|
||||
c = (c << 1) | ((x >> b) & 1);
|
||||
c = (c << 1) | ((y >> b) & 1);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate rotation diff D, that R (rotate) + D = T (targetRotate)
|
||||
* D is between -180, 180
|
||||
* @param rotate
|
||||
* @param targetRotate
|
||||
* @return
|
||||
*/
|
||||
public static float unifyRotationDiff(float rotate, float targetRotate) {
|
||||
float d = targetRotate - rotate;
|
||||
while(d >= 180){
|
||||
d -= 360;
|
||||
}
|
||||
while(d < -180){
|
||||
d += 360;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate rotation diff D, that R (rotate) + D = T (targetRotate)
|
||||
* D is between -180, 180
|
||||
* @param rotate
|
||||
* @param targetRotate
|
||||
* @return
|
||||
*/
|
||||
public static float unifyRotationTo360(float rotate) {
|
||||
while(rotate < 0){
|
||||
rotate += 360;
|
||||
}
|
||||
while(rotate > 360){
|
||||
rotate -= 360;
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param diff align difference between 2 angles ]-PI, PI]
|
||||
* @return
|
||||
*/
|
||||
public static double alignAngleDifference(double diff) {
|
||||
while(diff > Math.PI) {
|
||||
diff -= 2 * Math.PI;
|
||||
}
|
||||
while(diff <=-Math.PI) {
|
||||
diff += 2 * Math.PI;
|
||||
}
|
||||
return diff;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param diff align difference between 2 angles ]-180, 180]
|
||||
* @return
|
||||
*/
|
||||
public static double degreesDiff(double a1, double a2) {
|
||||
double diff = a1 - a2;
|
||||
while(diff > 180) {
|
||||
diff -= 360;
|
||||
}
|
||||
while(diff <=-180) {
|
||||
diff += 360;
|
||||
}
|
||||
return diff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
public class Simple {
|
||||
|
||||
public Simple() {}
|
||||
|
||||
public static int sum (int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue