ImageView onTouch not moving correctly - java

I'm having a problem moving an image from a place, where I touch it with a finger on a screen. It means when I touch it, let say in a center of a button and want to move it, it gets me to teh left a top corner of a button and from that point, I can move it around, up, left, right and so on...I want to have a possibility to move a button from wherever user put a finger on a button
Code:
public class Joystick extends RelativeLayout implements View.OnTouchListener {
private Context context;
private ImageView backgroundImageView;
private ImageView buttonImageView;
float xx = 0;
float yy = 0;
private static final String TRANSLATIONX = "setTranslationX";
public Joystick(Context context) {
super(context);
this.context = context;
initSlider();
if(buttonImageView != null) {
buttonImageView.setOnTouchListener(this);
}
this.setClipChildren(false);
}
public ImageView setView ( String imageName) {
ImageView image = new ImageView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
image.setLayoutParams(layoutParams);
// load image
try {
// get input stream
InputStream ims = getContext().getAssets().open(imageName);
// load image as Drawable
Drawable d = Drawable.createFromStream(ims, null);
// set image to ImageView
image.setImageDrawable(d);
} catch (IOException ex) {
}
return image;
}
public void initSlider() {
backgroundImageView = setView("background.png");
buttonImageView = setView("jostick.png");
addView(backgroundImageView);
addView(buttonImageView);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
float x = motionEvent.getRawX();
float y = motionEvent.getRawY();
float cx = buttonImageView.getWidth() / 2.f;
float cy = buttonImageView.getHeight() / 2.f;
float w = backgroundImageView.getWidth();
float h = backgroundImageView.getHeight();
double r = cx / 2.;
double dx = x - cx;
double dy = y - cy;
double hypot = Math.hypot(dx, dy);
double cos = dx / hypot; // cos
double sin = dy / hypot; // sin
double rcos = r * cos;
double rsin = r * sin;
double rdx = Math.abs(dx) < Math.abs(rcos) ? dx : rcos; // if,else
double rdy = Math.abs(dy) < Math.abs(rsin) ? dy : rsin;
Log.d("VALUES", "RAW X:" + motionEvent.getRawX() + ", X:" + motionEvent.getX() + ", CX:" + cx + ", CY:" + cy + ", dx:" + dx + ", dy:" + dy + ", Hypo:" + hypot + ", cos:" + cos + ", sin" + sin);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
view.setX(xx);
view.setY(yy);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
float transX = (float) (cx + rdx - w / 2.);
float transY = (float) (cy + rdy - h / 2.);
buttonImageView.setTranslationX(transX);
buttonImageView.setTranslationY(transY);
Log.d(TRANSLATIONX,"X:" + transX + ", Y:" + transY);
break;
}
return true;
}
}
Somewhere is a small bug. I hope for any help. Thanks

If anyone is interested or looking for some answer how to make a Joystick Button moving inside box, I have found a solution:
private Point calculate (float x, float y) {
float cx = buttonImageView.getWidth() / 2.f;
float cy = buttonImageView.getHeight() / 2.f;
double r = cx / 2.; // vrednost radius
double dx = x;
double dy = y;
double hypot = Math.hypot(dx, dy); // izracun hipotenuze
double cos = dx / hypot; // cos
double sin = dy / hypot; // sin
double rcos = r * cos;
double rsin = r * sin;
double rdx = Math.abs(dx) < Math.abs(rcos) ? dx : rcos; // if,else
double rdy = Math.abs(dy) < Math.abs(rsin) ? dy : rsin;
return new Point((int)rdx, (int)rdy);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final float x = motionEvent.getRawX(); // x točko
final float y = motionEvent.getRawY(); // y točka
//Log.d("VALUES", "RAW X:" + motionEvent.getRawX() + ", RAW Y:" + motionEvent.getRawY() + ", X:" + motionEvent.getX() + ", CX:" + cx + ", CY:" + cy + ", dx:" + dx + ", dy:" + dy + ", Hypo:" + hypot + ", cos:" + cos + ", sin" + sin);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
xDelta = view.getX() - x;
yDelta = view.getY() - y;
break;
case MotionEvent.ACTION_UP:
doBounceAnimation(buttonImageView);
doVibration();
view.setX(xx);
view.setY(yy);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
final float transX = (float) x;
final float transY = (float) y;
thread = new Thread() {
#Override
public void run() {
Point newPoint = calculate(transX+ xDelta,transY + yDelta);
buttonImageView.setX(newPoint.x);
buttonImageView.setY(newPoint.y);
}
};
thread.start();
Log.d(TRANSLATIONX,"X:" + transX + ", Y:" + transY);
break;
}
return true;
}
Enjoy coding!

Related

Android Studio movement emulation not working as expected

Can anyone tell me why my movement class x, y, and theta values are showing the values expected. But when it's emulated/run the 'nib' is mirroring all the movements instead of being right under the touch point. I don't want to change the math for the values if I don't have to, I just need to have the view show the same thing that the math is saying.
public class MovementDial extends AppCompatImageView {
public float x, y, r, Radius, _theta, xShift, yShift, offset;
private double distance, distanceAdj;
private RectF _knobRect = new RectF();
private PointF nibCenter, touchPoint;
private RectF nibRect = new RectF();
OnAngleChangedListener _angleChangedListener = null;
public interface OnAngleChangedListener {
void onAngleChanged(float theta);
}
public MovementDial(Context context) {
super(context);
nibCenter = new PointF(_knobRect.centerX(), _knobRect.centerY());
}
public float getTheta() {
return _theta;
}
public void setTheta (float theta){
_theta = theta;
invalidate();
}
public void setOnAngleChangedListener(OnAngleChangedListener listener) {
_angleChangedListener = listener;
}
private int map(double x, double in_min, double in_max, double out_min, double out_max) //#author: Aaron Pabst
{
int mapVal = (int) ((int)(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
return mapVal;
}
#Override
public boolean onTouchEvent(MotionEvent e) {
yShift = e.getY();
xShift = e.getX();
touchPoint = new PointF();
touchPoint.x = (int)(e.getX() - _knobRect.centerX());
touchPoint.y = map(yShift,0,(_knobRect.height()-_knobRect.centerY()),(_knobRect.height()-_knobRect.centerY()), 0);
x = touchPoint.x;
y = touchPoint.y;
float theta = mapThetaCoords(x,y); //maps out theta values between 0 to 360 using a traditional unit circle layout.
setTheta(theta);
double Radian = Math.toRadians(_theta);
final double PIValue = Math.PI/180;
Log.i("Touch", "initial Xvalue is :" +touchPoint.x);
Log.i("Touch", "initial Yvalue is :" +touchPoint.y);
Log.i ("Touch", "Touch point 3 changed to: " + theta);
distance = (float) Math.hypot(x, y); //polar coordinate radius (r = sqrt x^2 + y^2)
if(e.getAction() == MotionEvent.ACTION_DOWN) {
Log.i("Touch", "Xvalue touch is :" +touchPoint.x);
Log.i("Touch", "Yvalue touch is :" +touchPoint.y);
Log.i ("Touch", "Touch point 6 changed to: " + theta);
if(yShift == Radian)
if(distance <= Radius) {
nibCenter.x = _knobRect.centerX() - (float)(Radius * Math.cos(Math.toRadians(theta)));
nibCenter.y = _knobRect.centerY() - (float)(Radius * Math.sin(Math.toRadians(theta)));
x = touchPoint.x;
y = touchPoint.y;
Log.i("Touch", "Xvalue touch2 is :" +touchPoint.x);
Log.i("Touch", "Yvalue touch2 is :" +touchPoint.y);
Log.i("Touch", "Xvalue nib2 touch is :" +nibCenter.x);
Log.i("Touch", "Yvalue nib2 touch is :" +nibCenter.y);
} else if(distance > Radius) {
Log.i("Touch", "Xvalue touch3 is :" +touchPoint.x);
Log.i("Touch", "Yvalue touch3 is :" +touchPoint.y);
nibCenter.x = _knobRect.centerX() - (float)(Radius * Math.cos(Math.toRadians(theta)));
nibCenter.y = _knobRect.centerY() - (float)(Radius * Math.sin(Math.toRadians(theta)));
x = touchPoint.x;
y = touchPoint.y;
Log.i("Touch", "Xvalue nib1 touch is :" +nibCenter.x);
Log.i("Touch", "Yvalue nib1 touch is :" +nibCenter.y);
}
invalidate();
return true;
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
Log.i("Touch", "Xvalue2 move is :" + touchPoint.x);
Log.i("Touch", "Yvalue2 move is :" + touchPoint.y);
Log.i ("Touch", "Move value point 7 changed to: " + theta);
distanceAdj = distance;
distance = Math.min(distance, distanceAdj); //comparing changing polar radius values as coordinates change place.
if(distance <= Radius) { //check to see if polar radius coordinate is less or equal to the radius value
nibCenter.x = _knobRect.centerX() - (float)(distance * Math.cos(Math.toRadians(theta)));
nibCenter.y = _knobRect.centerY() - (float)(distance * Math.sin(Math.toRadians(theta)));
x = touchPoint.x;
y = touchPoint.y;
Log.i("Touch", "Xvalue move1 is :" + x);
} else if (distance > Radius){
Log.i("Touch", "Yvalue (greater than) move is :" + y);
nibCenter.x = _knobRect.centerX() - (float)(distance * Math.cos(Math.toRadians(theta) - 360));
nibCenter.y = _knobRect.centerY() - (float)(distance * Math.sin(Math.toRadians(theta) - 360));
x = touchPoint.x;
y = touchPoint.y;
Log.i("Touch", "Xvalue move2 is :" + x);
Log.i("Touch", "Yvalue move2 is :" + y);
}
invalidate();
return true;
} else if (e.getAction() == MotionEvent.ACTION_UP){
reset();
}
return false;
}
public float mapThetaCoords(float x, float y) {
double atan2 = Math.atan2(y , x);
float theta;
if (x >= 0 && y >= 0) { // Q 1
theta = (float) Math.toDegrees(atan2);
return theta;
} else if (x < 0 && y > 0) { // Q 2
theta = (float) Math.toDegrees(atan2);
return theta;
} else if (x < 0 && y < 0) { // Q 3
theta = (float) Math.toDegrees(atan2) + 360;
return theta;
} else if (x > 0 && y < 0) { // Q 4
theta = (float) Math.toDegrees(atan2) + 360;
return theta;
}
return 0;
}
public void reset(){
x = _knobRect.centerX()-(_knobRect.width()/2);
y = _knobRect.centerY()-(_knobRect.height()/2);
nibCenter.x = x;
nibCenter.y = y + _knobRect.centerY();
_theta = 0f;
distance = 0f;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
_knobRect.left = getPaddingLeft();
_knobRect.top = getPaddingTop();
_knobRect.right = getWidth()- getPaddingRight();
_knobRect.bottom = _knobRect.width();
offset = (getHeight() - _knobRect.height()) * 0.5f;
_knobRect.top += offset;
_knobRect.bottom += offset;
Radius = _knobRect.width() * 0.35f;
nibCenter.x = x + _knobRect.centerX();
nibCenter.y = y + _knobRect.centerY();
float nibRadius = Radius * 0.2f;
nibRect.left = nibCenter.x - nibRadius;
nibRect.top = nibCenter.y - nibRadius;
nibRect.right = nibCenter.x + nibRadius;
nibRect.bottom = nibCenter.y + nibRadius;
Paint knobPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
knobPaint.setColor(Color.BLACK);
Paint nibPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
nibPaint.setColor(Color.MAGENTA);
canvas.drawOval(_knobRect, knobPaint);
canvas.drawOval(nibRect, nibPaint);
}

Can't update the new position in the onTouch event

I'm a beginner, but I want to create a game. Now I am doing the moving function for my hero. When i touch the image and move it to the right side, the hero will go to the right side from the original position by 100dp per second, but its can't work.
Below is the code that i use as my moving function :
public View.OnTouchListener move = new View.OnTouchListener() {
private float x, y;
private int mx, my, hx, hy;
public boolean onTouch(final View v, MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_DOWN)) {
x = event.getX();
y = event.getY();
mx = (int) (event.getRawX() - x);
my = (int) (event.getRawY() - y);
} else if ((event.getAction() == MotionEvent.ACTION_MOVE)) {
hx = (int) (event.getRawX() - x);
hy = (int) (event.getRawY() - y);
v.layout(hx, hy, hx + v.getWidth(), hy + v.getHeight());
} else if ((event.getAction() == MotionEvent.ACTION_UP)) {
while (hx > mx) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mx += 100;
v.layout(mx, my, mx + v.getWidth(), my + v.getHeight());
}
}, 1000);
}
}
Log.e("x =", String.valueOf(x) + " y =" + String.valueOf(y));
Log.e("hx =", String.valueOf(hx) + " hy =" + String.valueOf(hy));
Log.e("mx =", String.valueOf(mx) + " my =" + String.valueOf(my));
return true;
}
};
Do this in ACTION_MOVE and ACTION_UP:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
//edit layout params according to your need
view.setLayoutParams(layoutParams);

How to Draw on android ImageView after scaling it?

I have an ImageView and i want to zoom/drag on by two fingers and draw on it using one finger , but after scaling ImageView , bitmap coordinates and ImageView coordinates doesn't match , to solve this i provided onTouch like this :
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
final int NONE = 0;
final int DRAW = 1;
final int PINCH = 2;
float x,y;
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
drawStartX = event.getX();
drawStartY = event.getY();
imageMode = DRAW;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//drag
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
x = event.getX(0) - event.getX(1);
y = event.getY(0) - event.getY(1);
oldDistance = (float) Math.sqrt(x * x + y * y);
if (oldDistance > 5f) {
savedMatrix.set(matrix);
x = event.getX(0) + event.getX(1);
y = event.getY(0) + event.getY(1);
mid.set(x / 2, y / 2);
imageMode = PINCH;
}
break;
case MotionEvent.ACTION_UP:
imageMode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
imageMode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (imageMode == DRAW) {
float drawEndX = event.getX();
float drawEndY = event.getY();
drawTest(drawStartX/bitmapScale - xTranslate, drawStartY/bitmapScale- yTranslate,drawEndX/bitmapScale - xTranslate,drawEndY/bitmapScale - yTranslate,0);
} else if (imageMode == PINCH) {
x = event.getX(0) - event.getX(1);
y = event.getY(0) - event.getY(1);
float newDistance = (float) Math.sqrt(x * x + y * y);
matrix.set(savedMatrix);
if(newDistance > 5f) {
//drag
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
//zoom
float scale = (newDistance / oldDistance);
matrix.postScale(scale, scale);
}
//get matrix
float[] mat = new float[9];
matrix.getValues(mat);
bitmapScale = mat[0];
xTranslate = mat[2];
yTranslate = mat[5];
}
break;
}
view.setImageMatrix(matrix);
return true;
}
drawTest is just a void that draws a line.
pic
in before scale picture i drag my finger on imageview and line drawn in exact position , but after scaling i dragged on same position but result is not right
but it's not working properly. how can i fix it ?

Android Java Zoom boundaries

I am developing an interactive video installation with touch screen control via an android device. I came up with the following code for panning, zooming and a pan limit so far. But I can't figure out how to limit the zooming (min/max) and how to avoid that the image get out of the boundaries (it should be displayed on the complete screen without any offset).
Thank you for your help!
public boolean onTouchEvent(MotionEvent me) {
switch (me.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
startPoint.set(me.getX(), me.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(me);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(midPoint, me);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float transX = me.getX() - startPoint.x;
float transY = me.getY() - startPoint.y;
matrix.set(savedMatrix);
float[] f = new float[9];
matrix.getValues(f);
float viewWidth = 1317.0f + 1317.0f * ((f[Matrix.MSCALE_X] - 1) / 1.1365f );
float viewWidth2 = 772.0f + 772.0f * ((f[Matrix.MSCALE_X] - 1) / 1.998f);
float viewHeight = 1097.0f + 1097.0f * ((f[Matrix.MSCALE_X] - 1) / 1.463f);
float viewHeight2 = 749.0f + 749.0f * ((f[Matrix.MSCALE_X] - 1) / 2.994f);
if (f[Matrix.MTRANS_X] + transX < -viewWidth){
Log.e("", "left bound");
transX = -f[Matrix.MTRANS_X] -viewWidth;
}
if(f[Matrix.MTRANS_X] + transX > -viewWidth2 ){
transX = -f[Matrix.MTRANS_X] -viewWidth2;
}
if (f[Matrix.MTRANS_Y] + transY > viewHeight){
transY = -f[Matrix.MTRANS_Y] + viewHeight;
}
if(f[Matrix.MTRANS_Y] + transY < viewHeight2){
transY = -f[Matrix.MTRANS_Y] + viewHeight2;
}
matrix.postTranslate(transX, transY);
} else if (mode == ZOOM) {
float newDist = spacing(me);
if (newDist > 10f) {
Log.e("MainActivity", "Correction: " + Float.toString(newDist / newDist));
float transX = me.getX() - startPoint.x;
float transY = me.getY() - startPoint.y;
matrix.set(savedMatrix);
float scale = newDist / oldDist;
float[] f = new float[9];
matrix.getValues(f);
matrix.postScale(scale, scale, -startPoint.x+300.f, -startPoint.y+900.0f);
if (f[Matrix.MSCALE_X] >= 270.0f) {
matrix.postScale((270.0f)/(f[Matrix.MSCALE_X]/scale), (270.0f)/(f[Matrix.MSCALE_Y]*scale), 0f ,0f);
mode = DRAG;
savedMatrix.set(matrix);
midPoint(midPoint, me);
} else if (f[Matrix.MSCALE_X] < 0.29f) {
matrix.postScale((0.29f)/(f[Matrix.MSCALE_X]/scale), (0.29f)/(f[Matrix.MSCALE_Y]*scale), 0f ,0f);
mode = DRAG;
savedMatrix.set(matrix);
midPoint(midPoint, me);
}
}
}
break;
}
// Matrix
try {
socket = new DatagramSocket();
// socket.setBroadcast(true);
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String dataString = matrix.toShortString();
Log.e("Activity_Main", dataString);
try {
DatagramPacket packet = new DatagramPacket(dataString.getBytes(), dataString.length(), serverAddr, PORT);
socket.send(packet);
socket.close();
} catch (Exception e) {
}
return true;
}
#SuppressLint("FloatMath")
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}

Application not working as expected

I have a standalone Java application below that is:
Generating a random line
Applied to a 2D grid where each cell value is the distance along the line perpindicular to the line
Finds the rise/run and attempts to calculate the original linear equation from the grid
Applies new line to another grid and prints out the greatest difference compared to the first grid
I expected the two grids to have identical values. The gradient lines may be different since the lines can extend outside the area of the grid, but should be similar and in two cases identical.
So is the problem a poor understanding of math, a bug in my code or a misunderstanding of floating point values?
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height - 1);
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
int endX = (int)Math.round(radius * Math.sin(theta));
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height - 1);
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a + b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a + b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE - 1, SIZE - 1, 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
I think I have fixed your program.
a) I took out the integer cast.
b) I removed all the 'x + 1' and 'x - 1' fudges you had used.
I think when dealing with floats and doubles, subtracting '1' from the end of a line is a No-No! What is 1 anyway? - it's ok to do this just before you plot it on the screen once it's an integer. But not while calculating! line length is a 'zero-based' quantity.
This version returns approx 4E-16 always.
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height );
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
float endX = (float)(radius * Math.sin(theta));
float endY = (float)(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height );
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a+b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a+b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE, SIZE , 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
why do you multiply by -1 at the end of this line?
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
this means that endY is always negative except radius is below 0. (cosinus always returns positive value)
is this intended or am i getting something wrong?
regards
You probably misunderstand float and/or double. This is a common problem with any language that implements the ieee spec for floats and doubles, which Java, C, C++ and just about every other language does.
Essentially
double val = 0;
for(int i=0;i<10;i++) {
val+=0.1;
System.out.println(val);
}
results in
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
And sometimes even worse. Either use BigDecimal, which alleviates a lot of the problem, or use integers.

Categories

Resources