How to write an intersects for Shapes in android - android

I have an written an Object called Shape which has a Point representing the topLeftCorner and a Dimension with represents its width and height.
To get the topRightCorner I can simply add the width to the topLeftPoint.x. I use to rotate them on a certain degree around their center. The problem after the rotation is, that my intersects(Shape) method fails, because it does not honor the rotation of the Shapes. The rotation will be the same for each Shape. My current implementation looks like this inside my Shape Object:
public boolean intersects(Shape s){
// functions returning a Point of shape s
return intersects(s.topLeft())
|| intersects(s.topRight())
|| intersects(s.bottomLeft())
|| intersects(s.bottomRight())
|| intersects(s.leftCenter())
|| intersects(s.rightCenter())
|| intersects(s.center());
}
public boolean intersects(Point p){
return p.x >= leftX()
&& p.x <= rightX()
&& p.y >= topY()
&& p.y <= bottomY();
}
Basically I need functions like rotatedLeftX() or rotatedTopRight() to work properly. Also for that calculation I think it doesn't matter when the topLeft point before a rotation of ie 90 will turn into topRight...
I already read this and this Question here, but do not understand it fully.

I modified an algorithm from Stackoverflow to do what you are indicating with rectangles for a battleship game I wrote. Here is the code:
private boolean overlaid(int [][][] shps, int curr)
{
for (int i = curr-1; i>=0; --i)
{
// From: http://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other/306332#306332
// if (RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 &&
// RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1)
if (shps[curr][0][1] <= shps[i][1][1] &&
shps[curr][1][1] >= shps[i][0][1] &&
shps[curr][0][0] <= shps[i][1][0] &&
shps[curr][1][0] >= shps[i][0][0])
return true;
}
return false;
}
private int [][][] shps = { {{-1,-1},{-1,-1}},
{{-1,-1},{-1,-1}},
{{-1,-1},{-1,-1}} };
The shps parameter is just a matrix indicating the location of the top-left and bottom-right corners of each rectangle using {x0,y0}, {x1,y1}. For example, shps[curr][0][1] == the current shp's y0. For my purposes, I had to use <= and >=. Also, you have to be mindful of the reverse of y if you are using screen coordinates versus Cartesian coordinates. Also DeMorgan's Law if you want to use NOT overlaid.

I have a solution:
lets assume I want to calculate the rotation (90 degrees) of a Shape with x =1, y=1 (topLeft Point) and a width of 4 and a height of 6 around its center (3, 4) == (x1, y2)
rotatedX = x1 + cos(q) * (x - x1) - sin(q) * (y - y1)
rotatedY = y1 + sin(q) * (x - x1) + cos(q) * (y - y1)
in this case:
rotatedX = 3 + cos(90) * (1 - 3) - sin(90) * (1 - 4)
rotatedY = 4 + sin(90) * (1 - 3) + cos(90) * (1 - 4)
this is for a rotation in a cartesian-plane (where positive rotation values means rotation counter-clockwise)
So if you wanna aply a rotation of 90 degrees CLOCKWISE you just have to multiply rotation with -1;

Related

Android libgdx bouncy screen bounds

I need to set phone's bounds as a wall where elements inside can bounce. The physic should be correct. Here an example :
I know that Interpolation allow bounce but I don't know how to create the physics
Check for the collisions with the walls. I'm assuming you're rendering Bitmaps, so the origin of the square we'll say is the top left corner. In that case:
if (x + width >= SCREEN_WIDTH || x <= 0) vx *= -1;
if (y + height >= SCREEN_HEIGHT || y <= 0) vy *= -1;
Where vx and vy are the x and y velocities.

Drawing stripes in a flag math formula

I have a rectangle with known size and position. (flag)
I have to fill this rectangle with 4 other rectangles. (stripes)
Each stripe must have 1/4 of the total width of the flag and his position is near the previous.
I have to draw this stripes with a random angle that goes from 0° to 90°.
0° = Vertical stripes (stripe width = flag width / 4)
90° = Horizontal stripes (stripe width = flag height / 4)
How can I calculate the width of each stripe for other angles?
int stripes = 4;
RectF rect = new RectF(0, 0, 100f, 75f);
float angle = new Random.nextInt(90);
float stripeSize;
if (angle == 0) {
stripeSize = rect.width() / stripes;
} else if (angle == 90) {
stripeSize = rect.height() / stripes;
} else {
stripeSize = ?
}
canvas.save();
canvas.rotate(angle, rect.centerX(), rect.centerY());
float offset = 0;
for (int i = 0; i < stripes; i++) {
if (angle == 0) {
reusableRect.set(offset, rect.top, offset + stripeSize, rect.bottom);
} else if (angle == 90) {
reusableRect.set(rect.left, offset, rect.right, offset + stripeSize);
} else {
reusableRect.set(?, ?, ?, ?);
}
canvas.drawRect(reusableRect, paint);
offset += stripeSize;
}
canvas.restore();
Let's pretend you have one stripe. Depending on the angle, the stripe width is going to be a value between the shorter dimension (the height in your case) and the longer dimension (the width in your case). The formula for the stripe width calculation should look something like this:
height + ((width - height) * ?)
where ? varies between 0 and 1 based on the angle of rotation. To me that sounds like the sine function might be a good candidate: sine(0) = 0 and sine(90) = 1. You can use Math.sin(), but be aware that the argument it takes is in radians, not degrees, so you need to use Math.toRadians() on your angle first. Then just divide by the number of stripes:
double radians = Math.toRadians(angle);
float stripeTotal = height + ((width - height) * Math.sin(radians));
float stripeWidth = stripeTotal / 4; // or however many stripes you have
If it's not perfect, you can adjust the formula. One last point, since these values only need to be calculated once, I would do that separately every time the angle changes (if it ever changes), not inside of onDraw().

Using Android accelerometer y axis falls through terrain - Unity3D

This works much better, thank you. However, it still does not work very well. While I don't go completely through the terrain, the FP controller seems to hover just below the terrain. Adding a +10 helps, but there is another strange issue:
I have the camera set up as a child of the FP controller, at 0,0,0. When the game begins to run, the Y value in the transform of the controller window goes steadily down, in minus numbers, while the camera Y value goes steadily up, in the positive direction. The Y values are mirrored. Any ideas of what is going on?
void Update () {
moveX = Input.acceleration.x * 1;
moveY = Input.acceleration.y * 1;
moveZ = (1+ (Input.acceleration.z));
transform.Translate (0, 0, 0); //transform.translate Moves the transform in the direction and distance of translation
temp = transform.position; //temp = the position of the transform in world space. World Space: the absolute XYZ coordinates of all objects
temp.y = terrainY; //y component of Vector3 (float)
transform.position = temp; //put the position of the transform in world space back into temp
terrainY = Terrain.activeTerrain.SampleHeight(temp); //Sample.height Samples the height at the given position defined in world space
temp2 = transform.position.y; //this shows transform.position.y axis is the same as terrainY, but not the same value as shown in the inspector
if (moveZ >= 0.055 && moveZ >= -0.1) {
zeroZFlag = 1;
if(moveZ >= 0.041){
moveZ = moveZ*10; //multiply by 10 to make it faster when going forward
if (moveY >= 0) {
transform.Translate (moveX,terrainY + 10,moveZ);
//transform.translate needs to be three floats. So the middle one needs to be the y value of the top of the terrain
}
if (moveY < 0){
transform.Translate (moveX,terrainY,-moveZ);
}
}
I have added to my code as suggested but am still having trouble getting the first person controller to stay on the same y-value as the terrain. In the following iteration of code, the First person y value leaves the world entirely and goes up forever.
My code uses
`transform.Translate (moveX,terrainY,moveZ);
to move the FP controller around, where moveX and moveY are acceleration values and terrainY theoretically should be the actual value of the Y-axis as shown in the transform box.
I think that the first issue is that I am mixing acceleration values (X,Z) with a terrain transform for Y, so different meaning to the values.
BTW X and Z axis move very well with the accelerometer with this code, but I will change everything if necessary!
I am not sure what kind of float value operation translate.transform does. The manual states that it returns in the space.world, but does it move by the number of units or position?
Here is my new code, and thank you in advance for help:
public float terrainY;
// y axis falls through terrain
// travels through walls even though collider is set
void LateUpdate (){
//terrainY = Terrain.activeTerrain.SampleHeight (transform.position);
}
void Update () {
moveX = Input.acceleration.x * 1;
moveY = Input.acceleration.y * 1;
moveZ = (1+ (Input.acceleration.z));
transform.Translate (0, 0, 0);
terrainY = Terrain.activeTerrain.SampleHeight (transform.position);
if (moveZ >= 0.055 && moveZ >= -0.1) {
zeroZFlag = 1;
if(moveZ >= 0.041){
moveZ = moveZ*10; //multiply by 10 to make it faster when going forward
if (moveY >= 0) {
transform.Translate (moveX,terrainY,moveZ);
//transform.translate needs to be three floats. so the middle one needs to be the y value of the top of the terrain
}
if (moveY < 0){
transform.Translate (moveX,terrainY,-moveZ);
}
My code causes me to go through and under my terrrain in Unity 3D because the y axis is always zero. In this world, x-axis is left/right and z is depth. Y axis is up/down and shold follow the mountains, hills, valleys in the terrain. However, it just goes through, at 0 height.
Does anyone know what variable/class should be in the y-axis spot instead of the "0" I have there? Thanks in advance!
public class Movement2 : MonoBehaviour {
public float moveX = Input.acceleration.x;
public float moveY = Input.acceleration.y;
public float moveZ = Input.acceleration.z;
public float Speed = 20.0f;
public int zeroZFlag;
void Update () {
moveX = Input.acceleration.x * 1;
moveY = Input.acceleration.y * 1;;
//moveZ = Mathf.Abs(1+ (Input.acceleration.z) * 20);
moveZ = (1+ (Input.acceleration.z));
transform.Translate (0, 0, 0);
if (moveZ >= 0.055 && moveZ >= -0.1) { zeroZFlag = 1;
if (moveY >= 0) {
transform.Translate (moveX,0,moveZ);
}
if (moveY < 0){
transform.Translate (moveX,0,-moveZ);
}
else {
zeroZFlag = 0;
}
}
Try using Terrain.SampleHeight(), it returns the height of the terrain with a given X and Z coordinate. Update its Y position to the height of the terrain every Update().
Reference: http://docs.unity3d.com/ScriptReference/Terrain.SampleHeight.html.
EDIT 1:
The reason your FP controller always goes up forever is because you keep translating it with a Y value.
transform.Translate(moveX, terrainY, moveZ)
// this way you keep adding terrainY value to the Y position
// thus it always goes up and will never end
While you need your FP controller to be constantly the same Y position with the terrain. You should instead modify the position of Y directly.
Vector3 temp = transform.position;
temp.y = terrainY;
transform.position = temp;
EDIT 2:
I try my best to help you see through your code:
void Update () {
moveX = Input.acceleration.x * 1;
moveY = Input.acceleration.y * 1;
moveZ = (1+ (Input.acceleration.z));
transform.Translate (0, 0, 0); // why do you need this? this basically does nothing
// you should get terrain height first to be used as temp.y below
terrainY = Terrain.activeTerrain.SampleHeight(transform.position);
temp = transform.position;
temp.y = terrainY + 10; // you said +10 helped, so I put it here
transform.position = temp;
if (moveZ >= 0.055 && moveZ >= -0.1) {
zeroZFlag = 1;
if(moveZ >= 0.041){
moveZ = moveZ*10;
if (moveY >= 0) {
// here I think you shouldn't move the Y anywhere,
// because you've updated position of Y in every update frame
// so you only need to move X and Z due to device tilt
transform.Translate (moveX,0,moveZ);
}
if (moveY < 0){
transform.Translate (moveX,0,-moveZ);
}
}
This also works:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CharacterController))]
public class Movement6 : MonoBehaviour {
public float speed = 30.0f;
public float moveX = Input.acceleration.x;
public float moveY = Input.acceleration.y;
public float moveZ = Input.acceleration.z;
void Update() {
moveX = Input.acceleration.x * 1;
moveY = Input.acceleration.y * 1;
moveZ = (1+ (Input.acceleration.z));
CharacterController controller = GetComponent<CharacterController>();
Vector3 forward = transform.TransformDirection(moveX,0,moveZ);
controller.SimpleMove (forward * speed);
}
}

Detecting touch events on shapes

I've got this graph-like circle that is divided in several "portions". I draw these using drawArc on a canvas.
canvas.drawArc(oval, startAngle, sweepAngle, true, fillPaint);
I would like to make these arcs clickeable so I can trigger events when the user taps them. So I decided to override onTouchEvent, then for ACTION_DOWN go through all my portions and check if the touch position lies within them.
I tried this code here which is answering to a similar question, but I am getting strange angle values (i.e. negative values for top right quadrant, values between 180-270 in the rest of them).
public boolean isInArea(float x, float y){
int deltaX = (int) (x - oval.centerX());
int deltaY = (int) (y - oval.centerY());
if (Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) > mRectShape.width()/2) {
return false;
}
// offset for radiant, depending on quadrant
double offset = 0;
if (deltaX > 0 && deltaY < 0) {
// top right -> nothing to do
} else if (deltaX > 0 && deltaY > 0) {
// bottom right -> add 90 degrees
offset = Math.PI/2;
} else if (deltaX < 0 && deltaY > 0) {
// bottom left -> add 180 degrees
offset = Math.PI;
} else if (deltaX < 0 && deltaY < 0) {
// top left -> add 270 degrees
offset = Math.PI * 3/2;
}
//now calculate angle
double angle = Math.asin(deltaY / deltaX);
double total = angle + offset;
double totalDeg = total * 180 / Math.PI;
Log.d(LOG_TAG, "angle :" + totalDeg);
if(totalDeg > mStartAngle && totalDeg < mSweepAngle){
return true;
}
return false;
}
I also tried without setting the offsets nand then I get negative values for top quadrants, and positive values between 0 and 180 in the bottom ones.
Also tried using atan2 like this:
angle = Math.atan2(deltaY, -deltaX);
What am I missing here?

Rotating an ImageView like a compass (with the "north pole" set elsewhere)

I'm stumped regarding how to implement a "personal compass", ie a compass that points to a specific bearing instead of the standard "north pole"... unfortunatly, my current attempt has come out wrong (doesn't point at the given bearing). It's also hooked up with the accelerator to be able to dynamically adjust itself based on which way the user is turning.
Here's my current attempt at it (the onSensorChanged()-method that updates the arrow):
public void onSensorChanged( SensorEvent event ) {
// If we don't have a Location, we break out
if ( LocationObj == null ) return;
float azimuth = event.values[0];
float baseAzimuth = azimuth;
GeomagneticField geoField = new GeomagneticField( Double
.valueOf( LocationObj.getLatitude() ).floatValue(), Double
.valueOf( LocationObj.getLongitude() ).floatValue(),
Double.valueOf( LocationObj.getAltitude() ).floatValue(),
System.currentTimeMillis() );
azimuth += geoField.getDeclination(); // converts magnetic north into true north
//Correct the azimuth
azimuth = azimuth % 360;
//This is where we choose to point it
float direction = azimuth + LocationObj.bearingTo( destinationObj );
rotateImageView( arrow, R.drawable.arrow, direction );
//Set the field
if( baseAzimuth > 0 && baseAzimuth < 45 ) fieldBearing.setText("S");
else if( baseAzimuth >= 45 && baseAzimuth < 90 ) fieldBearing.setText("SW");
else if( baseAzimuth > 0 && baseAzimuth < 135 ) fieldBearing.setText("W");
else if( baseAzimuth > 0 && baseAzimuth < 180 ) fieldBearing.setText("NW");
else if( baseAzimuth > 0 && baseAzimuth < 225 ) fieldBearing.setText("N");
else if( baseAzimuth > 0 && baseAzimuth < 270 ) fieldBearing.setText("NE");
else if( baseAzimuth > 0 && baseAzimuth < 315 ) fieldBearing.setText("E");
else if( baseAzimuth > 0 && baseAzimuth < 360 ) fieldBearing.setText("SE");
else fieldBearing.setText("?");
}
And here's the method that rotates the ImageView (rotateImageView()):
private void rotateImageView( ImageView imageView, int drawable, float rotate ) {
// Decode the drawable into a bitmap
Bitmap bitmapOrg = BitmapFactory.decodeResource( getResources(),
drawable );
// Get the width/height of the drawable
DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight();
// Initialize a new Matrix
Matrix matrix = new Matrix();
// Decide on how much to rotate
rotate = rotate % 360;
// Actually rotate the image
matrix.postRotate( rotate, width, height );
// recreate the new Bitmap via a couple conditions
Bitmap rotatedBitmap = Bitmap.createBitmap( bitmapOrg, 0, 0, width, height, matrix, true );
//BitmapDrawable bmd = new BitmapDrawable( rotatedBitmap );
//imageView.setImageBitmap( rotatedBitmap );
imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap));
imageView.setScaleType( ScaleType.CENTER );
}
Any help would be much appreciated, as I don't quite know how to proceed. The "readings" I'm getting while trying it out is somewhat inaccurate and points in the wrong direction. Am I doing something really off, or did I just have a really bad test-run?
Your rotateImageView function should work just fine, however there are some things that needs to be changed in your rotation calculations.
//This is where we choose to point it
float direction = azimuth + LocationObj.bearingTo( destinationObj );
rotateImageView( arrow, R.drawable.arrow, direction );
The problem is that bearingTo will give you a range from -180 to 180, which will confuse things a bit. We will need to convert this value into a range from 0 to 360 to get the correct rotation.
This is a table of what we really want, comparing to what bearingTo gives us
+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0 | 0 |
+-----------+--------------+
| 90 | 90 |
+-----------+--------------+
| 180 | 180 |
+-----------+--------------+
| -90 | 270 |
+-----------+--------------+
| -135 | 225 |
+-----------+--------------+
| -180 | 180 |
+-----------+--------------+
Even though the bearingTo is in the range -180 to 180, 0 is still true north which will leave us to this calculation:
// Store the bearingTo in the bearTo variable
float bearTo = LocationObj.bearingTo( destinationObj );
// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
if (bearTo < 0) {
bearTo = bearTo + 360;
}
If we add some dummy values to test our new formula:
float bearTo = -100;
// This will now equal to true
if (-100 < 0) {
bearTo = -100 + 360 = 360 - 100 = 260;
}
We've now sorted out the bearingTo, lets head on to the azimuth!
You need to substract the declination instead of adding it, as we want azimuth to be 0 when we point the phone directly at true north instead of having the declination added to the azimuth, which will then give us double the declination when we point the phone to true north. Correct this by subtracting the declination instead of adding it.
azimuth -= geoField.getDeclination(); // converts magnetic north into true north
When we turn the phone to true north now, azimuth will then equal to 0
Your code for correcting the azimuth is no longer necessary.
// Remove / uncomment this line
azimuth = azimuth % 360;
We will now continue to the point of where we calculate the real rotation. But first i will summarize what type of values we have now and explaining what they really are:
bearTo = The angle from true north to the destination location from the point we're your currently standing.
azimuth = The angle that you've rotated your phone from true north.
By saying this, if you point your phone directly at true north, we really want the arrow to rotate the angle that bearTo is set as. If you point your phone 45 degrees from true north, we want the arrow to rotate 45 degrees less than what bearTo is. This leaves us to the following calculations:
float direction = bearTo - azimuth;
However, if we put in some dummy values:
bearTo = 45;
azimuth = 180;
direction = 45 - 180 = -135;
This means that the arrow should rotate 135 degrees counter clockwise. We will need to put in a similiar if-condition as we did with the bearTo!
// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
direction = direction + 360;
}
Your bearing text, the N, E, S and W is off, so i've corrected them in the final method below.
Your onSensorChanged method should look like this:
public void onSensorChanged( SensorEvent event ) {
// If we don't have a Location, we break out
if ( LocationObj == null ) return;
float azimuth = event.values[0];
float baseAzimuth = azimuth;
GeomagneticField geoField = new GeomagneticField( Double
.valueOf( LocationObj.getLatitude() ).floatValue(), Double
.valueOf( LocationObj.getLongitude() ).floatValue(),
Double.valueOf( LocationObj.getAltitude() ).floatValue(),
System.currentTimeMillis() );
azimuth -= geoField.getDeclination(); // converts magnetic north into true north
// Store the bearingTo in the bearTo variable
float bearTo = LocationObj.bearingTo( destinationObj );
// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
if (bearTo < 0) {
bearTo = bearTo + 360;
}
//This is where we choose to point it
float direction = bearTo - azimuth;
// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
direction = direction + 360;
}
rotateImageView( arrow, R.drawable.arrow, direction );
//Set the field
String bearingText = "N";
if ( (360 >= baseAzimuth && baseAzimuth >= 337.5) || (0 <= baseAzimuth && baseAzimuth <= 22.5) ) bearingText = "N";
else if (baseAzimuth > 22.5 && baseAzimuth < 67.5) bearingText = "NE";
else if (baseAzimuth >= 67.5 && baseAzimuth <= 112.5) bearingText = "E";
else if (baseAzimuth > 112.5 && baseAzimuth < 157.5) bearingText = "SE";
else if (baseAzimuth >= 157.5 && baseAzimuth <= 202.5) bearingText = "S";
else if (baseAzimuth > 202.5 && baseAzimuth < 247.5) bearingText = "SW";
else if (baseAzimuth >= 247.5 && baseAzimuth <= 292.5) bearingText = "W";
else if (baseAzimuth > 292.5 && baseAzimuth < 337.5) bearingText = "NW";
else bearingText = "?";
fieldBearing.setText(bearingText);
}
You should be able to set the matrix to the ImageView without having to recreate the bitmap each time, and er.. 'normalise' (is that the word?) the readings.
float b = mLoc.getBearing();
if(b < 0)
b = 360 + b;
float h = item.mHeading;
if(h < 0)
h = 360 + h;
float r = (h - b) - 360;
matrix.reset();
matrix.postRotate(r, width/2, height/2);
In the above example mLoc is a Location returned by a gps provider and getBearing returns the number of degrees east of north of the current direction of travel. item.mHeading has been calculated using the Location.bearingTo() function using mLoc and the item's location. width and height are the dimensions of the image view.
So, make sure your variables are in degrees and not radians, and try 'normalising' (getting headings into the range of 0-360 and not -180-180). Also, if the results are off by 180 degrees, make sure you're getting the bearingTo your target, rather than the degrees from your target to you.
The above matrix can then be set in an ImageView that has a ScaleType.Matrix
imageView.setMatrix(matrix);
imageview.setScaleType(ScaleType.Matrix);
Since you're rotating around the centre point of the imageView (the width/2, height/2 in the postRotate), your drawable should be pointing upwards and will be rotated at draw time, rather than re-creating a new bitmap each time.
I spent about 40 hours one weekend trying to do this.
Pain in the butt, hopefully I can spare you that pain.
Ok, I am warning you, this is some ugly code.
I was in a pinch to finish it, it has no naming schemes, but i tried to comment it as best as I could for you.
It was used to locate large piles of nuts laying out in fields for storage
Using the phones current latitude and longitude, the lat/lon of the destination, the compass sensor, and some algebra, I was able to calculate the direction to the destination.
Lat/lon and sensor readings are pulled from the MainApplication class
This is some of the code for arrow.class, which I used to draw an arrow on a canvas towards a direction.
//The location you want to go to//
//"Given North"
double lat=0;
double lon=0;
//////////////////////////////////
protected void onDraw(Canvas canvas) {
//Sensor values from another class managing Sensor
float[] v = MainApplication.getValues();
//The current location of the device, retrieved from another class managing GPS
double ourlat= MainApplication.getLatitudeD();
double ourlon= MainApplication.getLongitudeD();
//Manually calculate the direction of the pile from the device
double a= Math.abs((lon-ourlon));
double b= Math.abs((lat-ourlat));
//archtangent of a/b is equal to the angle of the device from 0-degrees in the first quadrant. (Think of a unit circle)
double thetaprime= Math.atan(a/b);
double theta= 0;
//Determine the 'quadrant' that the desired location is in
//ASTC (All, Sin, Tan, Cos) Determines which value is positive
//Gotta love Highschool algebra
if((lat<ourlat)&&(lon>ourlon)){//-+
//theta is 180-thetaprime because it is in the 2nd quadrant
theta= ((Math.PI)-thetaprime);
//subtract theta from the compass value retrieved from the sensor to get our final direction
theta=theta - Math.toRadians(v[0]);
}else if((lat<ourlat)&&(lon<ourlon)){//--
//Add 180 degrees because it is in the third quadrant
theta= ((Math.PI)+thetaprime);
//subtract theta from the compass value retreived from the sensor to get our final direction
theta=theta - Math.toRadians(v[0]);
}else if((lat>ourlat)&&(lon>ourlon)){ //++
//No change is needed in the first quadrant
theta= thetaprime;
//subtract theta from the compass value retreived from the sensor to get our final direction
theta=theta - Math.toRadians(v[0]);
}else if((lat>ourlat)&&(lon<ourlon)){ //+-
//Subtract thetaprime from 360 in the fourth quadrant
theta= ((Math.PI*2)-thetaprime);
//subtract theta from the compass value retreived from the sensor to get our final direction
theta=theta - Math.toRadians(v[0]);
}
canvas.drawBitmap(_bitmap, 0, 0, paint);
float[] results = {0}; //Store data
Location.distanceBetween(ourlat, ourlon, lat, lon, results);
try{
//Note, pileboundary is a value retreived from a database
//This changes the color of the canvas based upon how close you are to the destination
//Green < 100 (or database value), Yellow < (100)*2, Otherwise red
if((results[0])<(pileboundary==0?100:pileboundary)){
_canvas.drawColor(Color.GREEN);
}else if((results[0])<(pileboundary==0?100:pileboundary)*2){
_canvas.drawColor(Color.YELLOW);
}else{
_canvas.drawColor(Color.rgb(0xff, 113, 116)); //RED-ish
}
//Draw the distance(in feet) from the destination
canvas.drawText("Distance: "+Integer.toString((int) (results[0]*3.2808399))+ " Feet", 3, height-3, textpaint);
}catch(IllegalArgumentException ex){
//im a sloppy coder
}
int w = canvas.getWidth();
int h = height;
int x = w / 2; //put arrow in center
int y = h / 2;
canvas.translate(x, y);
if (v != null) {
// Finally, we rotate the canvas to the desired direction
canvas.rotate((float)Math.toDegrees(theta));
}
//Draw the arrow!
canvas.drawPath(thearrow, paint);
}
//Some of my declarations, once again sorry :P
GeomagneticField gf;
Bitmap _bitmap;
Canvas _canvas;
int _height;
int _width;
Bitmap b;
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Get the current GeomagneticField (Should be valid until 2016, according to android docs)
gf = new GeomagneticField((float)lat,(float)lon,(float)MainApplication.getAltitude(),System.currentTimeMillis());
_height = View.MeasureSpec.getSize(heightMeasureSpec);
_width = View.MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(_width, _height);
_bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
b=Bitmap.createBitmap(_bitmap);
drawBoard();
invalidate();
}
//Here is the code to draw the arrow
thearrow.moveTo(0, -50);
thearrow.lineTo(-20, 50);
thearrow.lineTo(0, 50);
thearrow.lineTo(20, 50);
thearrow.close();
thearrow.setFillType(FillType.EVEN_ODD);
Hopefully you can manage to read my code... If I get time, I will make it a bit prettier.
If you need any explaining, let me know.
-MrZander

Categories

Resources