The bitmap only contain the text so the width and the height of the bitmap are determined by text's width and height and some math. now when i rotate the text using
canvas.rotate(tilt - 180, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
//the seek bar has a range of 0 to 360 so in order to get the right tilt i need to use (tilt - 180) instead of (tilt)
this is what happen
well obviously center of the rotation is wrong. I'v tried to use canvas.translate and the result have got better but they are yet not correct:
float actualTilt = Math.abs(tilt - 180);
actualTilt = (actualTilt >= 90 && actualTilt != 180) ? 90 - actualTilt % 90 : actualTilt;
if (actualTilt == 180) actualTilt = 0;
// by above statements i make sure actualTilt is less than 90 and it's values are 0,1,...89,90,89,88,...0
float v = actualTilt / 90;
canvas.translate((-(textWidth / 2) * v), (textHeight / 2) * v);
//i don't understand the above statement either but this is the result
if texts get longer the result get even worst
I found here the following codeto rotate a bitmap in a canvas:
Matrix matrix = new Matrix();
matrix.setRotate(angle, imageCenterX, imageCenterY);
yourCanvas.drawBitmap(yourBitmap, matrix, null);
Have you tried this out?
Related
I have a custom view where I load a bitmap image. Then I do some operations like zoom in, zoom out, rotation, drag and change position etc. After these operations I want to draw on the bitmap using finger. I use canvas.drawPath(mPath, mPaint) for this. If I load the image in the screen and start drawing it perfectly works, but if I change position of the image or rotate it or zoom in or out; my path is drawn in different position than I touched. My question is what can be the cause of this problem and how can I solve it.
Before making changes to the image you're displaying, save the canvas' position.
After, restore its original position.
Something like this :
public void onDraw(Canvas c) {
c.save();
//Do your "image manipulation" logic
c.restore();
c.drawPath(mPath, mPaint);
}
My problem is solved for dragging the image and repositioning and scaling up and down by the answer of this question.
I have put below code in onTouchEvent method.
float x = event.getX() / drawImage.getScaleX() - rect.left / drawImage.getScaleX();
float y = (event.getY()) / drawImage.getScaleY() - rect.top / drawImage.getScaleY();
float p = drawImage.getCenterX() / drawImage.getScaleX() - rect.left / drawImage.getScaleX();
float q = drawImage.getCenterY() / drawImage.getScaleY() - rect.top / drawImage.getScaleY();
float x1 = (float) ((x - p) * Math.cos(drawImage.getAngle()) + (y - q) * Math.sin(drawImage.getAngle()) + p);
float y1 = (float) ((y - q) * Math.cos(drawImage.getAngle()) - (x - p) * Math.sin(drawImage.getAngle()) + q);
Here drawImage is my custom drawable. x1, y1 are the actual coordinate I was searching for.
Good day.I am creating a siri like wave for android and i encounter an big issue.I need the wave to be in 4 colors.Lets assume i only have one single line which is drawing on the screen accordingly to the voice decibels.Anyway i am able to do it but no way i am able to give 4 different colors for same path.Assume it is 1 single path which moves from screen start to screen end,i need that line to have 4 different colors,mainly i had to divide the path into 4 parts and draw the color for each parts,but neither google,nor any other source give me anything (not even found anything similar to what i want).
Meanwhile i am posting the code where actually i am drawing the lines.
for (int l = 0; l < mWaveCount; ++l) {
float midH = height / 2.0f;
float midW = width / 2.0f;
float maxAmplitude = midH / 2f - 4.0f;
float progress = 1.0f - l * 1.0f / mWaveCount;
float normalAmplitude = (1.5f * progress - 0.5f) * mAmplitude;
float multiplier = (float) Math.min(1.0, (progress / 3.0f * 2.0f) + (1.0f / 3.0f));
if (l != 0) {
mSecondaryPaint.setAlpha((int) (multiplier * 255));
}
mPath.reset();
for (int x = 0; x < width + mDensity; x += mDensity) {
float scaling = 1f - (float) Math.pow(1 / midW * (x - midW), 2);
float y = scaling * maxAmplitude * normalAmplitude * (float) Math.sin(
180 * x * mFrequency / (width * Math.PI) + mPhase) + midH;
// canvas.drawPoint(x, y, l == 0 ? mPrimaryPaint : mSecondaryPaint);
//
// canvas.drawLine(x, y, x, 2*midH - y, mSecondaryPaint);
if (x == 0) {
mPath.moveTo(x, y);
} else {
mPath.lineTo(x, y);
// final float x2 = (x + mLastX) / 2;
// final float y2 = (y + mLastY) / 2;
// mPath.quadTo(x2, y2, x, y);
}
mLastX = x;
mLastY = y;
}
if (l == 0) {
canvas.drawPath(mPath, mPrimaryPaint);
} else {
canvas.drawPath(mPath, mSecondaryPaint);
}
}
I tried to change color on if (l == 0) {
canvas.drawPath(mPath, mPrimaryPaint);
} but if i change it here,no result at all,either the line is separate and not moving at all,but it should,either the color is not applied,propably because i am doing it in loop as i had to and everytime the last color is picked to draw.Anyway can you help me out?Even an small reference is gold for me because really there is nothing at all in the internet.
Anyway even though Matt Horst answer fully correct,i found the simplest and easiest solution...i never thought it would be so easy.Anyway if in world there is someone who need to make an path divided into multiple colors,here is what you can do
int[] rainbow = getRainbowColors();
Shader shader = new LinearGradient(0, 0, 0, width, rainbow,
null, Shader.TileMode.REPEAT);
Matrix matrix = new Matrix();
matrix.setRotate(90);
shader.setLocalMatrix(matrix);
mPrimaryPaint.setShader(shader);
Where getRainbowColors() is an array of colors you wish your line to have and width is the length of the path so the Shader knows how to draw the colors in right way to fit the length of path.Anyway .....easy isnt it?and pretty purged me a lot to get into this simple point.Nowhere in internet you could find only if you are looking for something completelly different,than you might come across this.
It seems to me like you could set up one paint for each section, each with a different color. Then set up one path for each section too. Then as you draw across the screen, wherever the changeover point is between sections, start drawing with the new path. And make sure first to use moveTo() on the new path so it starts off where the old one left off.
For my solution, I tried changing the color of the linePaint in the onDraw Call. But it was drawing a single color.
So i used two different paints for two different colors and draw path on the canvas.
And it worked. Hope it helps someone out there.
I have a rectangle with known size and position. (flag)
I have to fill this rectangle with 4 other rectangles. (stripes)
Each stripe must have 1/4 of the total width of the flag and his position is near the previous.
I have to draw this stripes with a random angle that goes from 0° to 90°.
0° = Vertical stripes (stripe width = flag width / 4)
90° = Horizontal stripes (stripe width = flag height / 4)
How can I calculate the width of each stripe for other angles?
int stripes = 4;
RectF rect = new RectF(0, 0, 100f, 75f);
float angle = new Random.nextInt(90);
float stripeSize;
if (angle == 0) {
stripeSize = rect.width() / stripes;
} else if (angle == 90) {
stripeSize = rect.height() / stripes;
} else {
stripeSize = ?
}
canvas.save();
canvas.rotate(angle, rect.centerX(), rect.centerY());
float offset = 0;
for (int i = 0; i < stripes; i++) {
if (angle == 0) {
reusableRect.set(offset, rect.top, offset + stripeSize, rect.bottom);
} else if (angle == 90) {
reusableRect.set(rect.left, offset, rect.right, offset + stripeSize);
} else {
reusableRect.set(?, ?, ?, ?);
}
canvas.drawRect(reusableRect, paint);
offset += stripeSize;
}
canvas.restore();
Let's pretend you have one stripe. Depending on the angle, the stripe width is going to be a value between the shorter dimension (the height in your case) and the longer dimension (the width in your case). The formula for the stripe width calculation should look something like this:
height + ((width - height) * ?)
where ? varies between 0 and 1 based on the angle of rotation. To me that sounds like the sine function might be a good candidate: sine(0) = 0 and sine(90) = 1. You can use Math.sin(), but be aware that the argument it takes is in radians, not degrees, so you need to use Math.toRadians() on your angle first. Then just divide by the number of stripes:
double radians = Math.toRadians(angle);
float stripeTotal = height + ((width - height) * Math.sin(radians));
float stripeWidth = stripeTotal / 4; // or however many stripes you have
If it's not perfect, you can adjust the formula. One last point, since these values only need to be calculated once, I would do that separately every time the angle changes (if it ever changes), not inside of onDraw().
I am planning to port the great 3D accordion effect found on iOS (https://github.com/xyfeng/XYOrigami) on Android.
so far I have something pretty close, here is a screenshot with "pinch-to-fold" to fold the view:
http://imageshack.us/photo/my-images/822/3daccordion.png
as you can see, it remains a space between 2 opposite "panels".
The process is simple:
take the canvas of the main Layout
create a Custom surface view which will replace all the view of the layout
set as background image the taken canvas
cut this canvas into X panels (here 16)
redraw the surfaceview with all the "panels" with a calculated matrix
recalculate the matrix while pinching to give the "folding effect"
here is the transformation matrix :
private Matrix getFoldingMatrix(int angle, int startFrom,int position) {
int move = (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth)) * position
) +1*position; //overlap of 1px the panels and not get space between panels
final Camera camera = new Camera();
final Matrix matrix = new Matrix();
camera.save();
camera.rotateY(angle);
camera.getMatrix(matrix);
if(position%2==1){
move += (int) (
(mWidth - (Math.cos(angle * Math.PI / 180) * mWidth))
) ;
matrix.preTranslate(-mWidth, -mHeight/2);
matrix.postTranslate(startFrom+mWidth-move, mHeight/2);
} else {
matrix.preTranslate(0f, -mHeight/2);
matrix.postTranslate(startFrom-move, mHeight/2);
}
camera.restore();
return matrix;
}
what am I getting wrong ? why is there still these spaces between opposite "panels"?
I m woring on an android opengl 1.1 2d game with a top view on a vehicule and a camera zoom relative to the vehicule speed. When the speed increases the camera zoom out to offer the player a best road visibility.
I have litte trouble finding the exact way to detect if a sprite is visible or not regarding his position and the current camera zoom.
Important precision, all of my game's objects are on the same z coord. I use 3d just for camera effect. (that's why I do not need frustrum complicated calculations)
here is a sample of my GLSurfaceView.Renderer class
public static float fov_degrees = 45f;
public static float fov_radians = fov_degrees / 180 * (float) Math.PI;
public static float aspect; //1.15572 on my device
public static float camZ; //927 on my device
#Override
public void onSurfaceChanged(GL10 gl, int x, int y) {
aspect = (float) x / (float) y;
camZ = y / 2 / (float) Math.tan(fov_radians / 2);
Camera.MINIDECAL = y / 4; // minimum cam zoom out (192 on my device)
if (x == 0) { // Prevent A Divide By Zero By
x = 1; // Making Height Equal One
}
gl.glViewport(0, 0, x, y); // Reset The Current Viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrix
gl.glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
GLU.gluPerspective(gl, fov_degrees, aspect , camZ / 10, camZ * 10);
GLU.gluLookAt(gl, 0, 0, camZ, 0, 0, 0, 0, 1, 0); // move camera back
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrix
gl.glLoadIdentity(); // Reset The Modelview Matrix
when I draw any camera relative object I use this translation method :
gl.glTranslatef(position.x - camera.centerPosition.x , position.y -camera.centerPosition.y , - camera.zDecal);
Eveything is displayed fine, the problem comes from my physic thread when he checks if an object is visible or not:
public static boolean isElementVisible(Element element) {
xDelta = (float) ((camera.zDecal + GameRenderer.camZ) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ)* Math.atan(GameRenderer.fov_radians));
//(xDelta and yDelta are in reallity updated only ones a frame or when camera zoom change)
Camera camera = ObjectRegistry.INSTANCE.camera;
float xMin = camera.centerPosition.x - xDelta/2;
float xMax = camera.centerPosition.x + xDelta/2;
float yMin = camera.centerPosition.y - yDelta/2;
float yMax = camera.centerPosition.y + yDelta/2;
//xMin and yMin are supposed to be the lower bounds x and y of the visible plan
// same for yMax and xMax
// then i just check that my sprite is visible on this rectangle.
Vector2 phD = element.getDimToTestIfVisibleOnScreen();
int sizeXd2 = (int) phD.x / 2;
int sizeYd2 = (int) phD.y / 2;
return (element.position.x + sizeXd2 > xMin)
&& (element.position.x - sizeXd2 < xMax)
&& (element.position.y - sizeYd2 < yMax)
&& (element.position.y + sizeYd2 > yMin);
}
Unfortunately the object were disapearing too soon and appearing to late so i manuelly added some zoom out on the camera for test purpose.
I did some manual test and found that by adding approx 260 to the camera z index while calculating xDelta and yDelta it, was good.
So the line is now :
xDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260)* Math.atan(GameRenderer.fov_radians));
Because it's a hack and the magic number may not work on every device I would like to understand what i missed. I guess there is something in that "260" magic number that comes from the fov or ration width/height and that could be set as a formula parameter for pixel perfect detection.
Any guess ?
My guess is that you should be using Math.tan(GameRenderer.fov_radians) instead of Math.atan(GameRenderer.fov_radians).
Reasoning:
If you used a camera with 90 degree fov, then xDelta and yDelta should be infinitely large, right? Since the camera would have to view the entire infinite plane.
tan(pi/2) is infinite (and negative infinity). atan(pi/2) is merely 1.00388...
tan(pi/4) is 1, compared to atan(pi/4) of 0.66577...