I really cant find any resource to know how to compute the bounding box of a set of points.
I have a float/int array of points for which I wish to compute the bounding box ( I want to know all the four corners of the rectangle of the bounding box ). How do I accomplish this?
You could loop through the array:
int minX = Integer.MAX_VALUE, minY, maxX, maxY = Integer.MAX_VALUE;
for (int i=0;i<myArray.length;i++){
if (myArray[i].x > maxX){
maxX = myArray[i].x;
} else if (myArray[i].x < minX) {
minX = myArray[i].x;
} else if (myArray[i].y > maxY){
maxY = myArray[i].y;
} else (myArray[i].y < minY) {
minY = myArray[i].y;(
}
}
You didn't say what kind of list you are using (array of points or whatever) so you'll need to adjust myArray[i].y and maxY = Integer.MAX_VALUE as required.
Computing AABB (Axis aligned bounding box) is fairly trivial. Just sort the points in each axis, find the min max on each axis. The intersection of the 4 lines from these points is your AAB rectangle.
Computing OBB (oriented bounding box) is slightly non trivial. Luckily there is a method on GestureUtils that does exactly that, namely :
GestureUtils.computeOrientedBoundingBox(float[] points)
pass it your float array of points and life is good :)
Although you didn't really specify what sort of points you were referring to, this bit of code should still work. I use it for creating a bounding box around the vertices of a model. It should also be noted that rotation should happen separate to loading the vertices. I'm actually fairly certain you can't detect rotation unless it's explicitly stated, or serialized with the vertex data. Also, AABB's and OBB's are technically—from a mathematical perspective—the same thing, as I proved here: https://stackoverflow.com/a/63094985/3214889. So even though your question specifically states Oriented Bounding Box, the following code will work for both. However, you will need to rotate the box afterwards; unless you serialize the rotation somehow.
public void FromVertices(Vertex[] vertices)
{
// Calculate Bounding Box
float minX = float.PositiveInfinity;
float maxX = float.NegativeInfinity;
float minY = float.PositiveInfinity;
float maxY = float.NegativeInfinity;
float minZ = float.PositiveInfinity;
float maxZ = float.NegativeInfinity;
for (int i = 0; i < vertices.Length; i++)
{
Vector3 vertex = vertices[i].Location;
// Check for maximum
if (vertex.X > maxX)
{
maxX = vertex.X;
}
if (vertex.Y > maxY)
{
maxY = vertex.Y;
}
if (vertex.Z > maxZ)
{
maxZ = vertex.Z;
}
// Check for Minimum
if (vertex.X < minX)
{
minX = vertex.X;
}
if (vertex.Y < minY)
{
minY = vertex.Y;
}
if (vertex.Z < minZ)
{
minZ = vertex.Z;
}
}
this.Minimum = new Vector3(minX, minY, minZ);
this.Maximum = new Vector3(maxX, maxY, maxZ);
}
Related
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);
}
}
I am trying to draw an arrow to point to objects in am image. I have been able to write code to draw the line but cant seem to be able to find a way to draw the arrowhead.The code I wrote to draw a dragabble line is as follows.I need to draw an arrowhead on ACTION_UP event to the direction in which the line is pointing
if(event.getAction() ==MotionEvent.ACTION_DOWN) {
if (count==1){
x1 = event.getX();
y1 = event.getY();
System.out.println(count+"count of value a;skd");
Toast.makeText(getApplicationContext(), ""+(radius+count), Toast.LENGTH_LONG).show();
Log.i(TAG, "coordinate x1 : "+String.valueOf(x1)+" y1 : "+String.valueOf(y1));
}
}
else if(event.getAction() ==MotionEvent.ACTION_MOVE){
imageView.setImageBitmap(bmp2);
x2 = event.getX();
y2 = event.getY();
posX=(float)(x1+x2)/2;
posY=(float)(y1+y2)/2;
radius=(float) Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))/2;
onDraw();
Toast.makeText(getApplicationContext(), ""+radius, Toast.LENGTH_LONG).show();
}
Hi, for anyone still needing help .This is how I did it in the end
float h=(float) 30.0;
float phi = (float) Math.atan2(y2 - y1, x2 - x1);
float angle1 = (float) (phi - Math.PI / 6);
float angle2 = (float) (phi + Math.PI / 6);
float x3 = (float) (x2 - h * Math.cos(angle1));
float x4 = (float) (x2 - h * Math.cos(angle2));
float y3 = (float) (y2 - h * Math.sin(angle1));
float y4 = (float) (y2 - h * Math.sin(angle2));
c.drawLine(x1, y1,x2,y2 ,pnt);
c.drawLine(x2, y2,x3,y3 ,pnt);
c.drawLine(x2, y2,x4,y4 ,pnt);
I got help from the accepted answer and ios section in stackoverflow
How I would do this is to find the slope of the line, which is drawn between two points(start and end). The slope would be (dy/dx), and that would be a good start point for your arrow. Assuming you want the base of the arrowhead to be perpendicular to the line of the arrow, to find the slope of the base you would find the opposite reciprocal of the slope of the line. for example, lets say that your line has a slope of 2. The slope for the base of your triangle would be (-1/2), because you do (1/(oldslope)) and multiply by -1. I don't know android very well, but if I remember correctly, in Java, you would use a drawPolygon method, and you would have to specify 4 points(3 unique and 1 the same as the first to close it). Given the slope of the base of the tip, we can get our first two points and our final point. You should know before you start the dimensions of the arrowhead you wish to draw, so in this case b will be the length of your baseline. If you take ϴ=arctan(dy/dx), that will give you an angle between the x axis and your baseline. With that ϴ value, you can do ydif = b*sin(ϴ) to get the difference in y value between the two base corners of your arrow. Doing the same thing but with xdif = b*cos(ϴ) gives you the difference in the x value between the two base points. If the location of the final point of the line that the user drew is, say, (x1, y1), then the locations of the basepoints of the triangle would be (x1-(xdif/2), y1-(ydif/2)) and (x1+(xdif/2), y1+(ydif/2)). These two points, p1 and p2, are the first, second, and fourth points in the draw polygon method. To find the third point, we need to find the angle of the original line, by doing ϴ=arctan(dy/dx), this time using your original dy/dx. with that angle. Before we finish the actual calculation of the point, you first have to know how far from the end of your line the tip of the arrow should actually be, in my case, I will use the var h and h = 10. To get the cordinate, (x,y), assuming the cordinate for the line tip is (x1, y1)you would do (x1+hcosϴ, y1+hsinϴ). Use that for the third value in drawPolygon(), and you should be done. sorry if I kind of rushed at the end, I got kind of tired of typing, comment if you need help.
If you managed to draw a line from the input event, you might additionally draw a triangle on its end indicating the direction.
On another project I drew a square everytime a magnetic point on a grid was touched (as you can see here) Sorry I can not provide you any sample code right now. But if that's a suitable approach for you, I might post it later.
Here is a good code, its not mine, It was a Java Graphics2D code that I converted to Canvas. All credit go to the original guy/lady who wrote it
private void drawArrowHead(Canvas canvas, Point tip, Point tail)
{
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
int tempX = tip.x ,tempY = tip.y;
//make arrow touch the circle
if(tip.x>tail.x && tip.y==tail.y)
{
tempX = (tip.x-10);
}
else if(tip.x<tail.x && tip.y==tail.y)
{
tempX = (tip.x+10);
}
else if(tip.y>tail.y && tip.x==tail.x)
{
tempY = (tip.y-10);
}
else if(tip.y<tail.y && tip.x==tail.x)
{
tempY = (tip.y+10);
}
else if(tip.x>tail.x || tip.x<tail.x)
{
int rCosTheta = (int) ((10)*Math.cos(theta)) ;
int xx = tip.x - rCosTheta;
int yy = (int) ((xx-tip.x)*(dy/dx) + tip.y);
tempX = xx;
tempY = yy;
}
double x, y, rho = theta + phi;
for(int j = 0; j < 2; j++)
{
x = tempX - arrowLength * Math.cos(rho);
y = tempY - arrowLength * Math.sin(rho);
canvas.drawLine(tempX,tempY,(int)x,(int)y,this.paint);
rho = theta - phi;
}
}
Just call this for both sides of your line and it will draw an arrow at each side!
Im using below code to draw line on bitmap canvas while finger touch move... here i posted partial code and it is working fine..
As shown in below image, the black and white bitmap erased on touch drag.. I made canvas transparent so the parent layout background(color image) is getting visible.
I want to know , how much area is erased(like 50% or 60% of bitmap ).. is there any way to find that?
//Erasing paint
mDrawPaint = new Paint();
mDrawPaint.setAntiAlias(true);
mDrawPaint.setDither(true);
mDrawPaint.setStyle(Paint.Style.STROKE);
mDrawPaint.setStrokeJoin(Paint.Join.ROUND);
mDrawPaint.setStrokeCap(Paint.Cap.ROUND);
mDrawPaint.setStrokeWidth(50);
mDrawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
BlurMaskFilter mBlur = new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL);
mDrawPaint.setMaskFilter(mBlur);
private void doDraw(Canvas c) {
c.drawBitmap(mBitmap, 0, 0,null );
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 1;
void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
canvas.drawPath(mPath, mDrawPaint ); //Erasing Black and white image
}
void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mDrawPaint);
// kill this so we don't double draw
mPath.reset();
}
Try to use Monte Carlo method to estimate percentage of transparent area. I think it is a fastest and easiest way to do this. Take about 50 (depends on accuracy you need) random pixels on your transparency mask and check their color. Then calc ans = TransparentPixelsCount/TestPixelCount.
It is very hard to calculate square of user's drawings using path coordinates. And it's quite long to iterate over all pixels. So, IMHO Monte Carlo is your choise.
To get an exact (and slow) answer, you need to inspect every pixel and count the number are transparent and divide by the total number of pixels. If your requirements allow for some estimation, it is probably best to sample the image.
You could downsize the image and run and the above procedure on the smaller image. That has the disadvantage that the scaling operation might be going through all the pixels making it slow. I would recommend a grid sampling, it is similar to downsizing, but skips over pixels. Basically, we evenly space x sample points on a grid over the image. Then count the number of sample points that are transparent. The estimate of transparent percentage is the total transparent samples/number of transparent samples. You can get reasonable accuracy (usually within 5%) with a small number, say 100, samples. Here is a code function that implements this method -- bm is the Bitmap and scale is the number of samples per axis, so setting scale = 10 gives 100 total samples (10x10 sampling grid over the image).
static public float percentTransparent(Bitmap bm, int scale) {
final int width = bm.getWidth();
final int height = bm.getHeight();
// size of sample rectangles
final int xStep = width/scale;
final int yStep = height/scale;
// center of the first rectangle
final int xInit = xStep/2;
final int yInit = yStep/2;
// center of the last rectangle
final int xEnd = width - xStep/2;
final int yEnd = height - yStep/2;
int totalTransparent = 0;
for(int x = xInit; x <= xEnd; x += xStep) {
for(int y = yInit; y <= yEnd; y += yStep) {
if (bm.getPixel(x, y) == Color.TRANSPARENT) {
totalTransparent++;
}
}
}
return ((float)totalTransparent)/(scale * scale);
}
For reference, the slow method that would give you the results by counting every pixel is below. It can be used for reference on testing the above estimator.
static public float percentTransparent(Bitmap bm) {
final int width = bm.getWidth();
final int height = bm.getHeight();
int totalTransparent = 0;
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
if (bm.getPixel(x, y) == Color.TRANSPARENT) {
totalTransparent++;
}
}
}
return ((float)totalTransparent)/(width * height);
}
A different approach on this: you can calculate the size of each path using ComputeBounds. Then it should be simple to compare this with the size of your view and decide the % of the drawing.
Jus you need to keep in mind that the path can be drawn over itself, so you need to be careful and handle that in the calculation.
Store all point x and y value in two different sorted sets, one for x value of point and other for y value of point.
The final value of your bound will be point(min_x,min_y) and point(max_x,max_y).
You need to detect the points lying inside the drawn polygon.
Here is the functions which takes array that contains all the drawn point, and second parameter are the points itself i.e. x ,y.
// Return true if the dot { x,y } is within any of the polygons in the list
function pointInPolygons( polygons, dot )
for (i=1, [polygons count] i++)
{
if (pointInPolygon( polygons[i], dot ))
return true
}
return false
end
// Returns true if the dot { x,y } is within the polygon
//defined by points table { {x,y},- --{x,y},{x,y},... }
function pointInPolygon( points, dot )
local i, j = #points, #points
local oddNodes = false
for i=1, #points do
if ((points[i].y < dot.y and points[j].y>=dot.y
or points[j].y< dot.y and points[i].y>=dot.y) and (points[i].x<=dot.x
or points[j].x<=dot.x)) then
if (points[i].x+(dot.y-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x)<dot.x) then
oddNodes = not oddNodes
end
end
j = i
end
return oddNodes
end
I have circular sprites and I need to check to see if they collide with any other circle. I tried:
public boolean collision(){
boolean collide=false;
if(spriteNum>0)
for(int x=0;x<spriteNum;x++)
if(yourSprite[spriteNum].collidesWith(yourSprite[x]))
collide=true;
return collide;
}
But that creates a rectangle around it which kind of throws it off. I could use the distance formula to manually calculate if two sprites are in contact, but that seems taxing and each sprite is attached with a circle physics body, meaning there centers are constantly moving (and I don't know how to find the center). Any ideas?
As Alexandru points out, no circle collision detection is supported by AndEngine so far. The best way is to implement it yourself. His solution works fine (fast), but just in case you need a bit more precision, I will post another approximation:
// There is no need to use Sprites, we will use the superclass Entity
boolean collidesWith(Entity circle){
final float x1 = this.getX();
final float y1 = this.getY();
final float x2 = circle.getX();
final float y2 = circle.getY();
final float xDifference = x2 - x1;
final float yDifference = y2 - y1;
// The ideal would be to provide a radius, but as
// we assume they are perfect circles, half the
// width will be just as good
final float radius1 = this.getWidth()/2;
final float radius2 = circle.getWidth()/2;
// Note we are using inverseSqrt but not normal sqrt,
// please look below to see a fast implementation of it.
// Using normal sqrt would not need "1.0f/", is more precise
// but less efficient
final float euclideanDistance = 1.0f/inverseSqrt(
xDifference*xDifference +
yDifference*yDifference);
return euclideanDistance < (radius1+radius2);
}
/**
* Gets an aproximation of the inverse square root with float precision.
* #param x float to be square-rooted
* #return an aproximation to sqrt(x)
*/
public static float inverseSqrt(float x) {
float xhalf = 0.5f*x;
int i = Float.floatToIntBits(x);
i = 0x5f3759df - (i>>1);
x = Float.intBitsToFloat(i);
x = x*(1.5f - xhalf*x*x);
return x;
}
Note I am not the author of the fast inverseSqrt method, it works in Java (and more precisely in Android) because of its floating point representation (see IEEE 754 floating point representation and Java float to byte representation).
For further research, see:
Quake3 fast inverse Sqrt origins
Fast inverse Sqrt implementation in Java
Because there is no circle collision detection in Andengine the only way is to calculate the distance between them
boolean collidesWithCircle(Sprite circle) {
float x1 = this.getX();
float y1 = this.getY();
float x2 = circle.getX();
float y2 = circle.getY();
double a = x1 - x2;
double b = y1 - y2;
double c = (a * a) + (b * b);
if (c <= this.getWidth()*this.getWidth())
return true;
else return false;
}
You can create circular bodies if you are using physics world by using PhysicsFactory.createCircularBody() method.
I need an advice how to achieve the following functionality under Android:
I need an image that represents something like a graph (from discrete math), with vertices and edges, where I can click every vertice or edge and fire a different action.
Please advise me how to achieve this (maybe with imagebuttons) or another approach to represent this functionality.
I was bored, so I coded up this crude example...
It assumes straight edges between points.
public class App extends Activity
{
PlotView plot;
#Override
public void onCreate(Bundle sis)
{
super.onCreate(sis);
plot = new PlotView(this);
setContentView(plot);
}
public class PlotView extends View
{
Paint paint1 = new Paint();
Paint paint2 = new Paint();
Point[] points = new Point[10];
public PlotView(Context context)
{
super(context);
paint1.setColor(Color.RED);
paint2.setColor(Color.BLUE);
for (int i = 0; i < points.length; i++)
{
points[i] = new Point();
points[i].x = (float) (Math.random() * 320);
points[i].y = (float) (Math.random() * 480);
}
Arrays.sort(points);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
for (int i = 0; i < points.length; i++)
{
if (i < points.length - 1)
{
canvas.drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, paint2);
}
canvas.drawCircle(points[i].x, points[i].y, 5, paint1);
}
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
float x = event.getX();
float y = event.getY();
int hitPoint = -1;
int closestLeft = -1;
int closestRight = -1;
for (int i = 0; i < points.length; i++)
{
float dx = x - points[i].x;
float dy = y - points[i].y;
if(i < points.length - 1)
{
if(points[i].x < x && x < points[i + 1].x)
{
closestLeft = i;
closestRight = i + 1;
}
}
if (Math.abs(dx) <= 16.0f && Math.abs(dy) <= 16.0f)
{
hitPoint = i;
break;
}
}
if (hitPoint != -1)
{
Toast.makeText(getContext(), "Hit Point: " + hitPoint, Toast.LENGTH_SHORT).show();
}
else
if(closestLeft != -1 && closestRight != -1)
{
float dx = points[closestLeft].x - points[closestRight].x;
float dy = points[closestLeft].y - points[closestRight].y;
final float u = ((x - points[closestLeft].x) * dx + (y - points[closestLeft].y) * dy) / (dx * dx + dy * dy);
float px = points[closestLeft].x + u * dx;
float py = points[closestLeft].y + u * dy;
if (Math.abs(x - px) <= 16.0f && Math.abs(y - py) <= 16.0f)
{
Toast.makeText(getContext(), "Hit Line Between: " + closestLeft + " & " + closestRight, Toast.LENGTH_SHORT).show();
}
}
}
}
return super.onTouchEvent(event);
}
public class Point implements Comparable<Point>
{
float x;
float y;
#Override
public int compareTo(Point other)
{
if (x < other.x) return -1;
if (x > other.x) return 1;
return 0;
}
}
}
}
I can imagine how to do this with SurfaceView:
create a Vertex class, which among other things, has an x,y coordinate representing where to draw the vertex. If your vertex was a png image of a circle, then the top-left x,y coordinates of the image are stored in the Vertex class.
Have all your verticies in a List, and iterate through and draw each vertex.
the edges are more complicated since they might criss-cross or curve around.
assuming they are straight lines, then you can have a Edge class that contains the starting x,y and ending x,y coordinates.
you can iterate through a List of Edges and draw the lines accordingly
In order to detect when a user clicks on them, you should override the onTouch method and check the event.rawX() and event.rawY() values to see if they match up to a Vertex or Edge class.
for a Vertex class, you can check if x <= event.rawX <= x + image_width and y <= event.rawY <= y + image_height
for an Edge, you can check if the event.rawX, event.rawY coordinates are found in the line formed by the two sets of coordinates you stored in the Edge class.
I've used a similar method to draw a set of nodes in a game. I'm not so sure how to do the edges though - the method I outline would only work if they were straight and do not criss-cross.
I am sure there is a better way to do this using openGL, but I have not used openGL before.
Hopefully you can get some ideas out of this.
I think you might be best off with a SurfaceView:
http://developer.android.com/reference/android/view/SurfaceView.html
And handling the onTouchEvent() as a whole for the surface, and mapping that to underlying entities in the image. If you're calculating the drawing the graph as you go should be easy to also create a map of tapable areas and grabbing the X and Y of the touch event to figure out if it corresponds to an element in the image.
If you literally have an image, as an already processed PNG for example, you would need some way to also carry in the touch event areas. Depends where that image comes in from.
According to android help, "drawing to a View, is your best choice when you want to draw simple graphics that do not need to change dynamically and are not part of a performance-intensive game." This is the right way to go when making a snake or a chess game, for instance. So I don't see a point in suggesting using a SurfaceView for this, it will just overcomplicate things.
For clickable areas you override public boolean onTouchEvent(MotionEvent event) where you manage x and y coordinates of the click for identifying the clicked area.