I have a screen (480x800), M(mx, my) is a static point, and N(nx,ny) is a dynamic point on screen. Position of N(nx, ny) depends position of touch. I want to determine position of P(?,?) and Q(?,?) to draw line 1 and line 2. line 2 is reflective line 1.
This is my code:
private Line l2;
#Override
public boolean onSceneTouchEvent(final Scene pScene,
final TouchEvent pSceneTouchEvent) {
if (this.mPhysicsWorld != null) {
switch (pSceneTouchEvent.getAction()) {
case TouchEvent.ACTION_DOWN:
// Get position
p1x = pSceneTouchEvent.getX();
p1y = pSceneTouchEvent.getY();
return true;
case TouchEvent.ACTION_MOVE:
// Remove instance of the old line
mScene.detachChild(l2);
p3x = pSceneTouchEvent.getX();
p3y = pSceneTouchEvent.getY();
Rectangle testR = new Rectangle(CAMERA_WIDTH / 2,
CAMERA_HEIGHT / 2, 20, 20,
getVertexBufferObjectManager());
l2 = new Line(CAMERA_WIDTH / 2, CAMERA_HEIGHT / 2, p3x, p3y,
getVertexBufferObjectManager());
l2.setColor(new Color(223f / 255f, 118f / 255f, 43f / 255f));
l2.setLineWidth(5);
mScene.attachChild(l2);
return true;
}
return false;
}
return false;
}
If you have other ways to resolve my issue. Please share with me. Thanks.
I didn't program with AndEngine for a while, but this isn't about AndEngine so I can give you a psedu code to solve your problem.
1) Check that nx < mx
2) Calculate the slope of line 1 :
line1slope = (my-ny)/(mx-nx)
3) Find P coordinates using the equation : y-y1 = m(x-x1)
Where m = line1slope
y1 = my
x1 = mx
y = 480 (P's y coordinate) ( if line1slope > 0 then y = 0)
And then you can find your x ( which is P's x coordinate)
line2slope = -1 * line1slope because they are reflective
Now again you need to find Q (You know the X = 0 so you need to find only the Y coordinate)
using the equation : y-y1 = m(x-x1)
Where m = line2slope
y1 = py
x1 = px
x = 0
And then you can find your y coordinate(which is Q's y coordinate)
Hope it helps.
Related
I am trying to implement zooming on a canvas which should focus on a pivot point. Zooming works fine, but afterwards the user should be able to select elements on the canvas. The problem is, that my translation values seem to be incorrect, because they have a different offset, than the ones where I don't zoom to the pivot point (zoom without pivot point and dragging works fine).
I used some code from this example.
The relevant code is:
class DragView extends View {
private static float MIN_ZOOM = 0.2f;
private static float MAX_ZOOM = 2f;
// These constants specify the mode that we're in
private static int NONE = 0;
private int mode = NONE;
private static int DRAG = 1;
private static int ZOOM = 2;
public ArrayList<ProcessElement> elements;
// Visualization
private boolean checkDisplay = false;
private float displayWidth;
private float displayHeight;
// These two variables keep track of the X and Y coordinate of the finger when it first
// touches the screen
private float startX = 0f;
private float startY = 0f;
// These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
// Also the offset from initial 0,0
private float translateX = 0f;
private float translateY = 0f;
private float lastGestureX = 0;
private float lastGestureY = 0;
private float scaleFactor = 1.f;
private ScaleGestureDetector detector;
...
private void sharedConstructor() {
elements = new ArrayList<ProcessElement>();
flowElements = new ArrayList<ProcessFlow>();
detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
/**
* checked once to get the measured screen height/width
* #param hasWindowFocus
*/
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!checkDisplay) {
displayHeight = getMeasuredHeight();
displayWidth = getMeasuredWidth();
checkDisplay = true;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
ProcessBaseElement lastElement = null;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mode = DRAG;
// Check if an Element has been touched.
// Need to use the absolute Position that's why we take the offset into consideration
touchedElement = isElementTouched(((translateX * -1) + event.getX()) / scaleFactor, (translateY * -1 + event.getY()) / scaleFactor);
//We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
//amount for each coordinates This works even when we are translating the first time because the initial
//values for these two variables is zero.
startX = event.getX() - translateX;
startY = event.getY() - translateY;
}
// if an element has been touched -> no need to take offset into consideration, because there's no dragging possible
else {
startX = event.getX();
startY = event.getY();
}
break;
case MotionEvent.ACTION_MOVE:
if (mode != ZOOM) {
if (touchedElement == null) {
translateX = event.getX() - startX;
translateY = event.getY() - startY;
} else {
startX = event.getX();
startY = event.getY();
}
}
if(detector.isInProgress()) {
lastGestureX = detector.getFocusX();
lastGestureY = detector.getFocusY();
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
break;
}
detector.onTouchEvent(event);
invalidate();
return true;
}
private ProcessBaseElement isElementTouched(float x, float y) {
for (int i = elements.size() - 1; i >= 0; i--) {
if (elements.get(i).isTouched(x, y))
return elements.get(i);
}
return null;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
if(detector.isInProgress()) {
canvas.scale(scaleFactor,scaleFactor,detector.getFocusX(),detector.getFocusY());
} else
canvas.scale(scaleFactor, scaleFactor,lastGestureX,lastGestureY); // zoom
// canvas.scale(scaleFactor,scaleFactor);
//We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
//because the translation amount also gets scaled according to how much we've zoomed into the canvas.
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
drawContent(canvas);
canvas.restore();
}
/**
* scales the canvas
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
Elements are saved with their absolute position on the canvas (with dragging in mind). I suspect that I don't take the new offset from the pivot point to translateX and translateY in consideration, but I can't figure out where and how I should do this.
Any help would be appreciated.
Okay, so you're basically trying to figure out where a certain screen X/Y coordinate corresponds to, after the view has been scaled (s) around a certain pivot point {Px, Py}.
So, let's try to break it down.
For the sake of argument, lets assume that Px & Py = 0, and that s = 2. This means the view was zoomed by a factor of 2, around the top left corner of the view.
In this case, the screen coordinate {0, 0} corresponds to {0, 0} in the view, because that point is the only point which hasn't changed. Generally speaking, if the screen coordinate is equal to the pivot point, then there is no change.
What happens if the user clicks on some other point, lets say {2, 3}? In this case, what was once {2, 3} has now moved by a factor of 2 from the pivot point (which is {0, 0}), and so the corresponding position is {4, 6}.
All this is easy when the pivot point is {0, 0}, but what happens when it's not?
Well, lets look at another case - the pivot point is now the bottom right corner of the view (Width = w, Height = h - {w, h}). Again, if the user clicks at the same position, then the corresponding position is also {w, h}, but lets say the user clicks on some other position, for example {w - 2, h - 3}? The same logic occurs here: The translated position is {w - 4, h - 6}.
To generalize, what we're trying to do is convert the screen coordinates to the translated coordinate. We need to perform the same action on this X/Y coordinate we received that we performed on every pixel in the zoomed view.
Step 1 - we'd like to translate the X/Y position according to the pivot point:
X = X - Px
Y = Y - Py
Step 2 - Then we scale X & Y:
X = X * s
Y = Y * s
Step 3 - Then we translate back:
X = X + Px
Y = Y + Py
If we apply this to the last example I gave (I will only demonstrate for X):
Original value: X = w - 2, Px = w
Step 1: X <-- X - Px = w - 2 - w = -2
Step 2: X <-- X * s = -2 * 2 = -4
Step 3: X <-- X + Px = -4 + w = w - 4
Once you apply this to any X/Y you receive which is relevant prior to the zoom, the point will be translated so that it is relative to the zoomed state.
Hope this helps.
I am trying to create a pad-like view in android. I got a circle that follows user's gestures and I am using distance to keep the circle of going outside the main circle of the pad control.
My problem is I want the circle to keep following the gesture, but to stay inside of the main circle. I am using the formula for finding a point using angle and radius, but it does some funky stuff.
I am translating the canvas, so that the center of the main circle is at 0, 0.
Here is the code:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(this.mainRadius, this.mainRadius);
canvas.drawCircle(0, 0, this.mainRadius, this.debugPaint);
canvas.drawCircle(this.handleX, this.handleY, this.handleRadius, this.handlePaint);
}
private void translateHandle(MotionEvent event) {
int x = (int) (event.getX() - this.mainRadius);
int y = (int) (event.getY() - this.mainRadius);
double distance = distanceFromCenter(x, y);
if (distance <= this.maxDistance) {
this.handleX = x;
this.handleY = y;
} else {
float angle = (float) Math.toDegrees(Math.atan2(y, x));
if (angle < 0)
angle += 360;
this.handleX = (int) ((this.mainRadius - this.handleRadius) * Math.cos(angle));
this.handleY = (int) ((this.mainRadius - this.handleRadius) * Math.sin(angle));
}
//onTranslateHandle(distance);
}
And here is the funky stuff in a gif image:
I cannot verify this change into your code snippet but do hope it gives some idea how to proceed further anyway;
private void translateHandle(MotionEvent event) {
float x = event.getX() - this.mainRadius;
float y = event.getY() - this.mainRadius;
double distance = distanceFromCenter(x, y);
if (distance > this.maxDistance) {
// If distance is i.e 2.0 and maxDistance is 1.0 ==> adjust is 0.5
// which repositions x and y making distance 1.0 maintaining direction
double adjust = this.maxDistance / distance;
x = (float)(x * adjust);
y = (float)(y * adjust);
}
this.handleX = (int)x;
this.handleY = (int)y;
}
I can update the answer where needed if this does not give any useful results.
Hi I'm doing and endless sidescroller game where the terrain looks like a tunnel which is infinite. I managed to randomly generate the tunnel using this code:
private void createPaths() {
if(startingPath) {
pathBottom.setLastPoint(0, canvasHeight);
pathTop.setLastPoint(0, 0);
slopeWidth = 0;
slopeHeight = generateRandomNumber(canvasHeight / 4, canvasHeight / 2);
lastX = 0;
lastY = canvasHeight - slopeHeight;
newX = lastX;
newY = lastY;
startingPath = false;
} else {
lastX = canvasWidth;
lastY = newY;
newX = lastX;
newY = canvasHeight - slopeHeight;
}
pathBottom.lineTo(lastX, lastY);
pathTop.lineTo(lastX, lastY - OFFSET);
do {
lastX = newX;
lastY = newY;
slopeWidth = generateRandomNumber(canvasWidth / 8, canvasWidth / 2);
newX += slopeWidth;
if(i % 2 == 0) {
slopeHeight = generateRandomNumber(canvasHeight / 12, canvasHeight / 6);
newY = canvasHeight - slopeHeight;
} else {
slopeHeight = generateRandomNumber(canvasHeight / 4, canvasHeight / 2);
newY = canvasHeight - slopeHeight;
}
pathBottom.cubicTo(
interpolateLinear(lastX, newX, 0.333f),
lastY,
interpolateLinear(lastX, newX, 0.666f),
newY,
newX,
newY);
pathTop.cubicTo(
interpolateLinear(lastX, newX, 0.333f),
lastY - OFFSET,
interpolateLinear(lastX, newX, 0.666f),
newY - OFFSET,
newX,
newY - OFFSET);
i++;
} while (newX < canvasWidth * 2);
pathBottom.lineTo(newX, canvasHeight);
pathTop.lineTo(newX, 0);
}
and scroll it using:
public void updateTerrain() {
moveX -= speed;
int pos = newX - canvasWidth + moveX;
if(pos > 0) {
Matrix matrix = new Matrix();
matrix.setTranslate(-speed, 0);
pathBottom.transform(matrix);
pathTop.transform(matrix);
} else {
createPaths();
moveX = 0;
}
}
The problem is: the longer the path is the game becomes more "choppy". I think I should reduce the points that are being draw in the path after some time but to be honest I have no idea how to do it and still let the terrain scroll and generate. I would be grateful if you could help me. Thanks.
This looks like a small piece of a larger piece of logic. The performance issue may lie in some other code not shown here.
The general advice ( according to people like Romain Guy and Chet Haase ) is to avoid object allocation ( aka new ) during onDraw. Any "new" has the potential to trigger GC.
I would re-use the same instance of Matrix and just update it.
Also, as fadden mentioned "fixed-size sliding window structure" ( similar to circular buffer or ring buffer ) the comment above, you should ensure that your Path objects are a fixed size.
Pick a fixed number of points for the path ( let's say 200 )
Keep track of those points in an array and keep a "startindex" variable to track of the "logical" start of the array. When you need to add a new point, increment the index modulo the array size, overwrite the last point ( index - 1 modulo array size ). When you get to the end of the array you have to wrap ( start back at the beginning and go to startindex - 1 ).
Use path.incReserve to preallocate the memory when the view is created.
Use path.rewind to reset the path
Then re-use the same Path instance, to re-add all your points from your array of points ( starting at the "startIndex" )
Hello I searched in the forum,but coudn't find a helpful answer.
I'm making a game with AndEngine and I'm stuck for 3 days on shooting from rotating sprite.
That is my code and how I rotate the gun.I tried here to shoot a bullet ,but it shoots from a wrong starting point I would want to shoot a bullet from the end of the gun.
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
if(pSceneTouchEvent.isActionMove()){
final float dX = pSceneTouchEvent.getX() - machine.getX();
final float dY = pSceneTouchEvent.getY() - machine.getY();
float angle = (float) Math.atan2(dX,dY);
float rotation = MathUtils.radToDeg(angle) + 1;
machine.setRotation(rotation - 90);
Log.d("BUG",machine.getRotation() + "");
if(machine.getRotation() >= 84 ){
machine.setRotation(84);
}
if(machine.getRotation() <= -54 ){
machine.setRotation(-54);
}
final int incrementXValue = 15;
long sElapsed = System.currentTimeMillis() - lastFire;
if(bulletsAmout > 0 && sElapsed > cooldownBetweenShoot * cdModd){
e = new Entity(0,machine.getY());
e.setRotation(getRotation());
SceneManager.getInstance().getCurrentScene().attachChild(e);
float x2 = (float) (machine.getSceneCenterCoordinates()[0] + machine.getWidth() /2 * Math.cos(machine.getRotation()));
float y2 = (float) (machine.getSceneCenterCoordinates()[1] + machine.getWidth() /2 * Math.sin(machine.getRotation()));
float realX = (float) (Math.toRadians(x2) + machine.getWidth());
realY = (float) Math.toRadians(y2);
bullets = new Sprite(realX,realY, resourcesManager.bulletRegion.deepCopy(), vbom){
protected void onManagedUpdate(float pSecondsElapsed) {
float currentX = this.getX();
this.setX(currentX + incrementXValue);
super.onManagedUpdate(pSecondsElapsed);
}
};
bullets.setScale(0.06f);
e.attachChild(bullets);
projectilesToBeAdded.add(bullets);
bulletsAmout--;
lastFire = System.currentTimeMillis();
setBulletsText(bulletsAmout);
resourcesManager.pistolSound.play();
}
return true;
}
return false;
}
Assuming you are using GLES2-AnchorCenter:
You can position the bullet by setting it to the position of the end of the gun that you can get by calling gun.convertLocalToSceneCoordinates(gunMuzzleX, gunMuzzleY).
Then set the bullets rotation to the rotation of the gun.
apply velocity to the bullet. Calculate the speed-vector as follows FloatMath.sin(rotationOfBulletInRadians) * speed and FloatMath.cos(rotationOfBulletInRadians) * speed.
Be aware that you have to pass the rotation in radians to the sin and cos function NOT in degrees!
So I found how to fix that.
The problem is in this line of code :
e = new Entity(0,machine.getY());
Should be :
e = new Entity(machine.getX() - (machine.getHeight() / 2),machine.getY())
i have problem with detection road lanes with my phone.
i wrote some code for road lanes detection, but him not working for me.
From camera get modifications from normal view to BGR colors and try use GausianBlur and Canny, but i think i not good draw lanes for detection.
Maybe some people have another idea how detection road lanes with OpenCV?
Mat mYuv = new Mat(height + height / 2, width, CvType.CV_8UC1);
Mat mRgba = new Mat(height + height / 2, width, CvType.CV_8UC1);
Mat thresholdImage = new Mat(height + height / 2, width, CvType.CV_8UC1);
mYuv.put(0, 0, data);
Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420p2BGR, 4);
//convert to grayscale
Imgproc.cvtColor(mRgba, thresholdImage, Imgproc.COLOR_mRGBA2RGBA, 4);
// Perform a Gaussian blur (convolving in 5x5 Gaussian) & detect edges
Imgproc.GaussianBlur(mRgba, mRgba, new Size(5,5), 2.2, 2);
Imgproc.Canny(mRgba, thresholdImage, VActivity.CANNY_MIN_TRESHOLD, VActivity.CANNY_MAX_THRESHOLD);
Mat lines = new Mat();
double rho = 1;
double theta = Math.PI/180;
int threshold = 50;
//do Hough transform to find lanes
Imgproc.HoughLinesP(thresholdImage, lines, rho, theta, threshold, VActivity.HOUGH_MIN_LINE_LENGTH, VActivity.HOUGH_MAX_LINE_GAP);
for (int x = 0; x < lines.cols() && x < 1; x++){
double[] vec = lines.get(0, x);
double x1 = vec[0],
y1 = vec[1],
x2 = vec[2],
y2 = vec[3];
Point start = new Point(x1, y1);
Point end = new Point(x2, y2);
Core.line(mRgba, start, end, new Scalar(255, 0, 0), 3);
}
This approach is fine and I've done something similar, not for road line detection but I did notice that it could be used for that purpose. Some comments:
Not sure why you do:
Imgproc.cvtColor(mRgba, thresholdImage, Imgproc.COLOR_mRGBA2RGBA, 4);
as 1. the comment say convert to greyscale, which is a single channel and 2. thresholdImage will get overwritten with the call to Canny later. You just need to dimension thresholdImage with:
thresholdImage = new Mat(mRgba.size(), CvType.CV_8UC1);
What are your parameter values to the call to Canny? I played about with mine considerably and ended up with values like: threshold1 = 441, threshold2 = 160, aperture = 3.
Likewise Imgproc.HoughLinesP: I use Imgproc.HoughLines rather than Imgproc.HoughLinesP with parameters: threshold = 80, minLen = 30, maxLen = 10.
Also have a look at:
for (int x = 0; x < lines.cols() && x < 1; x++){
&& x < 1 means you will only take the first line that the call to HoughLinesP returns. I'd suggest you remove this and use some other criteria to reduce the number of lines; for example, I was interesting in only horizontal and vertical lines so I used atan2 to calculate line angles and exclude those that deviate too much.
UPDATE
Here is how I get the angle of a line. Assuming coordinates of one point is (x1,y1) and the other (x2, y2) then to get the angle:
double lineAngle = Math.atan2(y2 - y1, x2 - x1);
this should return an angle in radians between -PI/2 and PI/2
With regard Canny parameters then I would experiment - I set up onTouch so that I could adjust the threshold values by touching certain parts of the screen to see the effects in realtime. Note that aperture is rather disappointing parameter: it seems to only like odd values 3, 5 and 7 and 3 is the best that I've found.
Something like in the onTouch method:
int w = mRgba.width();
int h = mRgba.height();
float x = event.getX();
float y = event.getY();
if ((x < w / 3) && (y < h / 2)) t1 += 20;
if ((x < w / 3) && (y >= h / 2)) t1 -= 20;
if ((x > 2 * w / 3) && (y < h / 2)) t2 += 20;
if ((x > 2 * w / 3) && (y >= h / 2)) t2 -= 20;
t1 and t2 being the threshold values passed to the Canny call.