There are few actors on stage, lines are drawn connecting centers of them, like a graph, Nodes and edges. Nodes are gradable. On Drag I am using the following code
public void touchDragged(InputEvent event, float eventOffsetX, float
eventOffsetY, int pointer)
{
float deltaX = eventOffsetX - self.grabOffsetX;
float deltaY = eventOffsetY - self.grabOffsetY;
self.moveBy(deltaX, deltaY);
moveCoordinatesBy((int)deltaX,(int)deltaY);
}
the Method moveCoordinatesBy is updating the coordinate of the center of the node. which is used to draw lines (Edges) connecting the. This is working Fine.
My problem is - When I am using moveTo action for Nodes, I need to update edges at the same time. For this I need to update coordinates of there center. To do this I am using the following code in the act method of the Node -
public void act(float dt)
{
super.act(dt);
Vector2 loc = new Vector2();
loc.x = self.getX()+ self.getWidth()/2;
loc.y = self.getY() + self.getHeight()/2;
Vector2 v = new Vector2();
v = self.localToStageCoordinates(loc);
setCoordinates((int)v.x, (int)v.y);
}
The coordinates I am getting are not center of the node, they are almost multiplied by 2. Am I doing something wrong?
And also getWidth() and self.getHeight() returning double the size. When actor (node) is scaled getWidth() and self.getHeight() are returning arbitery values depending on how for it is located on stage. Further from (0,0) larger the returned value.
v = self.localToStageCoordinates(loc);
above line of code not needed, beacuse all the actors were directly added to the stage. By removing localToStageCoordinates(loc) its working fine now.
Related
I am currently working on a Worms-like game. I generate random Levels, which holds an Array of Points for each x with corresponding y. Also i have two arrays with some x values, where I then place trees and huts. I use a Orthographic camera and the translate method to move the camera when the user touches the screen. In order to have big levels, I decided to render the map only for the part that is currently visible. for that I have a BackgroundActor, which gets the current position of the camera, from that information I get the corresponding part of my map from the level class with the surface array. I then render this information with a ShapeRenderer. Then I render the props (trees and huts). The problem is, that the props get unaligned with the surface, when I drag the screen. For example: I move the map to the left, and the surface is moving faster to the left than the props. I already tried to set the projection Matrix for both the SpriteBatch and the ShapeRenderer, but it did not help.
Code:
#Override
public void draw(Batch batch, float parentAlpha) {
setBounds(); //gets the index for my map array from the camera
ShapeRenderer shapeRenderer = AndroidLauncher.gameScreen.shapeRenderer;
batch.end(); //needed, because otherwise the props do not render
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
for (int x = 0; x < ScreenValues.screenWidth; x++) {
int y = level.getYForX(x + leftBound);
shapeRenderer.setColor(level.getUndergroundColor());
shapeRenderer.rectLine(x, 0, x, y - level.getSurfaceThickness(), 1);
shapeRenderer.setColor(level.getSurfaceColor());
shapeRenderer.rectLine(x, y - level.getSurfaceThickness(), x, y, 1);
}
shapeRenderer.end();
batch.begin();
for (int x = 0; x < ScreenValues.screenWidth; x++) {
int y = level.getYForX(x + leftBound);
if (level.getPropForX(x) != Level.PROP_NONE) {
if (level.getPropForX(x) == Level.PROP_TREE) y -= 10;
Image imageToDraw = getImageFromPropId(level.getPropForX(x)); //Images are setup in the create method of my Listener
imageToDraw.setPosition(x, y);
imageToDraw.draw(batch, parentAlpha);
}
}
}
I fixed the issue myself. In the for loop for the props I needed to run x from leftBound to ScreenValues.screenWidth + leftBound. This still gives me Texture popping when the props get to the left side of the screen, because the props x position is out of screen, but this will be a small fix.
I currently have the following code:
private void drawGreen(Canvas canvas) {
greenPaint.setColor(0xFF00AA00);
if (start) {
greenPath = new Path();
greenPath.reset();
greenPath.moveTo(pathArrayX.get(0), pathArrayY.get(0));
start = false;
}
if (isInsideCircle(pathArrayX.get(pathIndex), pathArrayY.get(pathIndex), curX, curY, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics()))) {
greenPath.lineTo(pathArrayX.get(pathIndex), pathArrayY.get(pathIndex));
canvas.drawPath(greenPath, greenPaint);
pathIndex++;
}
}
private boolean isInsideCircle(float x, float y, float centerX, float centerY, float radius) {
return Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2) < Math.pow(radius, 2);
}
In my app, I at first draw a red path, with its coordinates stored in the ArrayLists pathArrayX and pathArrayY. I am tracking the X and Y coordinates of a circular ImageView being moved underneath a mouse, and would like to overlay the red path with a green path when the user hovers over the path from beginning to end. As the user hovers over the red path, the portion of the red path that they already completed would be overlaid by a green path along the same segment. The X and Y coordinates of the ImageView (curX and curY) are being calculated from a running thread.
However, my app doesn't appear to be drawing the green path at all. Is there anything I am doing wrong here?
Is this function even being called?
Assuming it's being called inside onDraw(Canvas), it looks like it might be missing the outer code for a loop. Seeing that you're doing pathIndex++ at the end, were you using a while loop? If you're just going to loop through point, use a for-loop as while-loop is more prone to dropping into an endless loop if you forgot to increment counter or doing wrongly, or do it in multiple places.
Side notes: if the boolean start flag is only being used to lazily initialise greenPath, you should scrap that and just use if (greenPath == null){ as a general practise. Use states that you can directly infer from objects and not use flags if you can help it, this makes code cleaner.
Background
I'm developing an app for Android that plots data as a line graph using AndroidPlot. Because of the nature of the data, it's important that it be pannable and zoomable. I'm using AndroidPlot's sample code on bitbucket for panning and zooming, modified to allow panning and zooming in both X and Y directions.
Everything works as desired except that there are no X and Y axis lines. It is very disorienting to look at the data without them. The grid helps, but there's no guarantee that grid lines will actually fall on the axis.
To remedy this I have tried adding two series, one that falls on just the X axis and the other on the Y. The problem with this is that if one zooms out too far the axis simply end, and it becomes apparent that I have applied a 'hack'.
Question
Is it possible to add X and Y axis lines to AndroidPlot? Or will my sad hack have to do?
EDIT
Added tags
I figured it out. It wasn't trivial, took a joint effort with a collaborator, and sucked up many hours of our time.
Starting with the sample mentioned in my question, I had to extend XYPlot (which I called GraphView) and override the onPreInit method. Note that I have two PointF's, minXY and maxXY, that are defined in my overridden XYPlot and manipulated when I zoom or scroll.
#Override
protected void onPreInit() {
super.onPreInit();
final Paint axisPaint = new Paint();
axisPaint.setColor(getResources().getColor(R.color.MY_AXIS_COLOR));
axisPaint.setStrokeWidth(3); //or whatever stroke width you want
XYGraphWidget oldWidget = getGraphWidget();
XYGraphWidget widget = new XYGraphWidget(getLayoutManager(),
this,
new SizeMetrics(
oldWidget.getHeightMetric(),
oldWidget.getWidthMetric())) {
//We now override XYGraphWidget methods
RectF mGridRect;
#Override
protected void doOnDraw(Canvas canvas, RectF widgetRect)
throws PlotRenderException {
//In order to draw the x axis, we must obtain gridRect. I believe this is the only
//way to do so as the more convenient routes have private rather than protected access.
mGridRect = new RectF(widgetRect.left + ((isRangeAxisLeft())?getRangeLabelWidth():1),
widgetRect.top + ((isDomainAxisBottom())?1:getDomainLabelWidth()),
widgetRect.right - ((isRangeAxisLeft())?1:getRangeLabelWidth()),
widgetRect.bottom - ((isDomainAxisBottom())?getDomainLabelWidth():1));
super.doOnDraw(canvas, widgetRect);
}
#Override
protected void drawGrid(Canvas canvas) {
super.drawGrid(canvas);
if(mGridRect == null) return;
//minXY and maxXY are PointF's defined elsewhere. See my comment in the answer.
if(minXY.y <= 0 && maxXY.y >= 0) { //Draw the x axis
RectF paddedGridRect = getGridRect();
//Note: GraphView.this is the extended XYPlot instance.
XYStep rangeStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.RANGE,
paddedGridRect, getCalculatedMinY().doubleValue(),
getCalculatedMaxY().doubleValue());
double rangeOriginF = paddedGridRect.bottom;
float yPix = (float) (rangeOriginF + getRangeOrigin().doubleValue() * rangeStep.getStepPix() /
rangeStep.getStepVal());
//Keep things consistent with drawing y axis even though drawRangeTick is public
//drawRangeTick(canvas, yPix, 0, getRangeLabelPaint(), axisPaint, true);
canvas.drawLine(mGridRect.left, yPix, mGridRect.right, yPix, axisPaint);
}
if(minXY.x <= 0 && maxXY.x >= 0) { //Draw the y axis
RectF paddedGridRect = getGridRect();
XYStep domianStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.DOMAIN,
paddedGridRect, getCalculatedMinX().doubleValue(),
getCalculatedMaxX().doubleValue());
double domainOriginF = paddedGridRect.left;
float xPix = (float) (domainOriginF - getDomainOrigin().doubleValue() * domianStep.getStepPix() /
domianStep.getStepVal());
//Unfortunately, drawDomainTick has private access in XYGraphWidget
canvas.drawLine(xPix, mGridRect.top, xPix, mGridRect.bottom, axisPaint);
}
}
};
widget.setBackgroundPaint(oldWidget.getBackgroundPaint());
widget.setMarginTop(oldWidget.getMarginTop());
widget.setMarginRight(oldWidget.getMarginRight());
widget.setPositionMetrics(oldWidget.getPositionMetrics());
getLayoutManager().remove(oldWidget);
getLayoutManager().addToTop(widget);
setGraphWidget(widget);
//More customizations can go here
}
And that was that. I sure wish this was built into AndroidPlot; it'll be nasty trying to fix this when it breaks in an AndroidPlot update...
I am trying to make a simple face detection app consisting of a SurfaceView (essentially a camera preview) and a custom View (for drawing purposes) stacked on top. The two views are essentially the same size, stacked on one another in a RelativeLayout. When a person's face is detected, I want to draw a white rectangle on the custom View around their face.
The Camera.Face.rect object returns the face bound coordinates using the coordinate system explained here and the custom View uses the coordinate system described in the answer to this question. Some sort of conversion is needed before I can use it to draw on the canvas.
Therefore, I wrote an additional method ScaleFacetoView() in my custom view class (below) I redraw the custom view every time a face is detected by overriding the OnFaceDetection() method. The result is the white box appears correctly when a face is in the center. The problem I noticed is that it does not correct track my face when it moves to other parts of the screen.
Namely, if I move my face:
Up - the box goes left
Down - the box goes right
Right - the box goes upwards
Left - the box goes down
I seem to have incorrectly mapped the values when scaling the coordinates. Android docs provide this method of converting using a matrix, but it is rather confusing and I have no idea what it is doing. Can anyone provide some code on the correct way of converting Camera.Face coordinates to View coordinates?
Here's the code for my ScaleFacetoView() method.
public void ScaleFacetoView(Face[] data, int width, int height, TextView a){
//Extract data from the face object and accounts for the 1000 value offset
mLeft = data[0].rect.left + 1000;
mRight = data[0].rect.right + 1000;
mTop = data[0].rect.top + 1000;
mBottom = data[0].rect.bottom + 1000;
//Compute the scale factors
float xScaleFactor = 1;
float yScaleFactor = 1;
if (height > width){
xScaleFactor = (float) width/2000.0f;
yScaleFactor = (float) height/2000.0f;
}
else if (height < width){
xScaleFactor = (float) height/2000.0f;
yScaleFactor = (float) width/2000.0f;
}
//Scale the face parameters
mLeft = mLeft * xScaleFactor; //X-coordinate
mRight = mRight * xScaleFactor; //X-coordinate
mTop = mTop * yScaleFactor; //Y-coordinate
mBottom = mBottom * yScaleFactor; //Y-coordinate
}
As mentioned above, I call the custom view like so:
#Override
public void onFaceDetection(Face[] arg0, Camera arg1) {
if(arg0.length == 1){
//Get aspect ratio of the screen
View parent = (View) mRectangleView.getParent();
int width = parent.getWidth();
int height = parent.getHeight();
//Modify xy values in the view object
mRectangleView.ScaleFacetoView(arg0, width, height);
mRectangleView.setInvalidate();
//Toast.makeText( cc ,"Redrew the face.", Toast.LENGTH_SHORT).show();
mRectangleView.setVisibility(View.VISIBLE);
//rest of code
Using the explanation Kenny gave I manage to do the following.
This example works using the front facing camera.
RectF rectF = new RectF(face.rect);
Matrix matrix = new Matrix();
matrix.setScale(1, 1);
matrix.postScale(view.getWidth() / 2000f, view.getHeight() / 2000f);
matrix.postTranslate(view.getWidth() / 2f, view.getHeight() / 2f);
matrix.mapRect(rectF);
The returned Rectangle by the matrix has all the right coordinates to draw into the canvas.
If you are using the back camera I think is just a matter of changing the scale to:
matrix.setScale(-1, 1);
But I haven't tried that.
The Camera.Face class returns the face bound coordinates using the image frame that the phone would save into its internal storage, rather than using the image displayed in the Camera Preview. In my case, the images were saved in a different manner from the camera, resulting in a incorrect mapping. I had to manually account for the discrepancy by taking the coordinates, rotating it counter clockwise 90 degrees and flipping it on the y-axis prior to scaling it to the canvas used for the custom view.
EDIT:
It would also appear that you can't change the way the face bound coordinates are returned by modifying the camera capture orientation using the Camera.Parameters.setRotation(int) method either.
In AndEngine game, I want to throw a ball physics body. The user sets its angle and force and throws it. Its scenario is same as we can see in Angry Birds. I have calculated both the force and angle but am confused how to apply both simultaneously on ball means the ball should be thrown at calculated angle but with particular force. Can anybody guide me to the right direction?
Here is my code snippet:
#Override
public boolean onAreaTouched(TouchEvent event,ITouchArea pTouchArea, float x, float y) {
// TODO Auto-generated method stub
if(event.isActionDown()) {
......
}
else if(event.isActionMove()) {
......
}
else if(event.isActionCancel() || event.isActionOutside() || event.isActionUp()) {
.....
launchHero(hero, string1.getX1()/PIXEL_TO_METER_RATIO_DEFAULT, string1.getY1()/PIXEL_TO_METER_RATIO_DEFAULT, x/PIXEL_TO_METER_RATIO_DEFAULT, y/PIXEL_TO_METER_RATIO_DEFAULT);
}
public void launchHero(Hero hero, float originX, float originY, float fingerX, float fingerY) {
Vector2 shoot = new Vector2((originX - fingerX), -(originY - fingerY));
shoot = shoot.nor().mul(10);
hero.getBody().setLinearVelocity(shoot);
}
}
return false;
}
I have added negative to (originY - fingerY) because if I don't do this, the ball first goes down then after colliding with base, it goes up.
It's very simple, use Body.setLinearVelocity(Vector2 pVector). Both direction and force are determined by the vector's parameters. Here's a sample code:
Vector2 shoot = new Vector2((originX - fingerX), (originY - fingerY));
body.setLinearVelocity(shoot);
This will shoot the body in the direction made by the finger and place of origin, or, in the case of Angry Birds, the finger and the sling.
If you want to make the force constant and multiply it by some number, you can do this before shooting:
shoot = shoot.nor().mul(multiplier);
You can use
body.applyLinearImpulse(Vector2 impulse, Vector2 point)
I prefer this function on setLinearVelocity because it gives more possibilities. To use this function you need to give the impulse in both coordinates and the body's point where to apply it.