How to draw a circular line circle like this image?
How to achieve or any suggestions?
Not essential, but I'd like to draw it on Android.
You can draw this by simply drawing colored rectangles. Either create a method to draw a rect and call it for each of the rectangles or create a buffer holding all the vertex data and call it once.
For the first procedure the easiest way is using matrices. Assume you are drawing a rectangle at angle of height and width with some inner circle radius. Now all you need is a single rect vertex buffer as:
{
0.0, -0.5,
0.0, 0.5,
1.0, -0.5,
1.0, 0.5
}
This represents a 1x1 cube which is positioned perfectly to draw a line. Now to use matrices we want to identity which would draw the line in the center of your screen. Then rotate it to whatever angle you need, then move it forward so it is outside of the inner circle and in the end scale it to whatever size you need:
Matrix4x4 matrix = Matrix4x4.identity // Start with identity
matrix = matrix.rotate(angle, 0, 0, 1) // Rotate around Z
matrix = matrix.translate(radius, 0, 0) // Translate it forward, this is not a bug
matrix = matrix.scale(height, width, 1.0) // Scale it
This should be it. Now if you want to add some performance on drawing you should have a single draw call so you create a single buffer. You may still use exactly the same procedure but transform original vertex data by a target matrix on CPU and then pack the result into array.
I am not sure from your image if the bottom (inner) part of lines needs to be circular. To achieve this the easiest way would be to draw to alpha mask first and then use it in blending. To achieve this you need to clear color to (1,1,1,1). Then draw a circle only on alpha channel using color mask (FALSE, FALSE, FALSE, TRUE) and blending (ONE, ONE) then using a any transparent color like (0,0,0,0). This will make your background completely white but there will be a circle in the middle with zero alpha. So now reset back to default mask settings using (TRUE, TRUE, TRUE, TRUE) and start drawing your rectangles with blending (DST_ALPHA, ONSE_MINUS_DST_ALPHA). This should cut your rectangles nicely to show a correct circle in the middle.
Some more robust procedures exist for the same thing like drawing to stencil buffer instead of alpha channel.
Related
I'm currently working on an OpenGL project in Android. Right now, I'm trying to create a "clip" for items to be confined to when drawing, using the stencil buffer. My problem is that I would like to be able to create a clip shape, and then "remove" portions of that clipped shape later on (much like in an Android Canvas calling clipPath() with Region.Op.UNION and then another clipPath() call with Region.Op.DIFFERENCE)
Basically, my intention is to only draw to portions of the screen with a stencil buffer bit of value 1. Therefore, as I understand it, I should be able to draw to the stencil buffer with different glStencilOp functions that will allow me to draw with 1's in some areas, and then perhaps replace them later on with the default 0 in portions of those areas.
As an example test, I tried to clip a "doughnut" on the screen, using something like this (Note: I am using the ShapeRenderer from the LibGDX open source project to draw shapes):
// Beginning LibGDX shapeRenderer.
shapeRenderer.begin(ShapeType.FILLED);
gl20.glClear(GL20.GL_STENCIL_BUFFER_BIT); // Clear stencil buffer.
gl20.glEnable(GL20.GL_STENCIL_TEST); // Enable stencil test.
gl20.glColorMask(false, false, false, false); // Disable color drawing.
gl20.glStencilMask(0xFF); // Set 0xFF as stencil mask.
// Have stencil test always fail, replace drawn stencil bits with 1.
gl20.glStencilFunc(GL20.GL_NEVER, 1, 0xFF);
gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_KEEP,GL20.GL_KEEP);
/* Draw outer circle, libGDX call. 600px radius. */
shapeRenderer.circle(screenRect.width() / 2, screenRect.height() / 2, 600);
// Now, replace newly drawn stencil bits with 0.
gl20.glStencilOp(GL20.GL_ZERO, GL20.GL_KEEP, GL20.GL_KEEP);
/* Draw inner circle, libGDX call. 200px radius. */
shapeRenderer.circle(screenRect.width() / 2, screenRect.height() / 2, 200);
// Re-enable color drawing.
gl20.glColorMask(true, true, true, true);
// Now, items drawn will be confined to areas with '1' stencil bit.
gl20.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
gl20.glStencilMask(0x00);
/* Draw a filled, colored rectangle to the entire screen. */
shapeRenderer.setColor(Color.RED);
shapeRenderer.drawRect(screenRect);
shapeRenderer.end();
Now, as I understand it, this should result in a colored 'doughnut' drawn to the screen; However, as the code is written above, nothing is drawn to the screen.
However, if I only clip the outer circle (and not try to get rid of the inner circle with the "gl20.glStencilOp(GL20.GL_ZERO, GL20.GL_KEEP, GL20.GL_KEEP)" call and subsequent inner circle drawing, a circle the size of the intended outer circle draws on the screen.
I've looked through the documentation that I could find, and everything that I've read so far indicates that this should work. Is there something that I'm missing here? Any help is greatly appreciated.
I've found the problem; I was drawing the two circles in between one begin() and end() call in the ShapeRenderer. Therefore, both circles were being drawn at the time of the end() call. This means that the shapes weren't even drawing to the stencil buffer, seeing as I was calling these functions before the end() call:
// Now, items drawn will be confined to areas with '1' stencil bit.
gl20.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
gl20.glStencilMask(0x00);
If I make sure that I use a begin() call before and end() call after each shape being drawn, it will draw at the expected time, therefore making sure that it is drawing the expected values to the stencil buffer.
The logic looks OK - what geometry are you drawing?
At a guess, based on the fact that you see the outer circle rendered on screen when you don't render the inner one, it looks like you are using the outer circle geometry in both cases (so you set all the stencil values, then clear all of them, so nothing has a stencil value of 1 when you come to blit the rectangle).
I implemented your solution in WebGL and it works fine (try it out):
http://ezekiel.vancouver.wsu.edu/~cs442/stackoverflow/stencil.html
Here is how I am mimicking your code:
gl.enable(gl.STENCIL_TEST);
gl.colorMask(false, false, false, false);
gl.stencilMask(0xFF);
gl.stencilFunc(gl.NEVER, 1, 0xFF);
gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP);
fillCircle(1); // radius of 1 maps to canvas width
gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP);
fillCircle(0.333);
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.EQUAL, 1, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
gl.stencilMask(0x00);
gl.uniform3fv(program.color, [1, 0, 0]);
fillCircle(3); // fill whole canvas width red
You'll see you get exactly what you wanted.
How to draw on canvas like this pic with alpha gradient?
Please specify next time whether you mean the android app canvas or HTML5 canvas on android browsers. If its the former, use android-canvas. This solution is in JS since its easier to show, and will work fine on either platform.
Gradients along paths in canvas are hard. The easiest way is to fudge it.
Instead of thinking of your image as a gradient that follows a circular path, think of it as two linear gradients.
One on the left side, going from green to gray, top to bottom.
The other on the right side, going from white to gray, top to bottom.
Imagine a square made of those two gradients:
Now imagine a circle cutting through:
That's all you gotta do.
To "cut" through like that its easiest to use clipping regions, so I've made an example doing that.
Here's the live example: http://jsfiddle.net/simonsarris/Msdkv/
Code below:
var greenPart = ctx.createLinearGradient(0,0,0,100);
greenPart.addColorStop(0, 'palegreen');
greenPart.addColorStop(1, 'lightgray');
var whitePart = ctx.createLinearGradient(0,0,0,100);
whitePart.addColorStop(0, 'white');
whitePart.addColorStop(1, 'lightgray');
var width = 20;
ctx.lineWidth = width;
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the left half
ctx.strokeStyle = greenPart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// Then we make a clipping region for the right half
ctx.save();
ctx.beginPath();
ctx.rect(50, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the right half
ctx.strokeStyle = whitePart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
I am drawing an arc with a border by painting two arcs, one over the other the first being slightly larger.
The issue is with "slightly larger" this can end up with the border not always being even all the way round.
Both the arcs I am drawing have the same radius, I simply make it larger by adding a degree to the start and two degrees to the end (necessary to ensure the borders on either end of the arc are equal) and increasing the stroke width.
In the supplied picture the thicker border edge is the smallest I can possibly make it while it is still visible. (-1 degree off the inner arc)
I have considered drawing the arc outline with four separate calls two straight lines for either end and two arcs. This seems quite inefficient for what I want to achieve.
I am wondering if anyone has any suggestions about how else I could draw a border thats even, minimizing the number of draw/canvas rotation calls if possible.
Relevant code sample for current solution:
Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFillPaint.setStyle(Style.STROKE);
mFillPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mFillPaint.setColor(Color.TRANSPARENT);
mFillPaint.setStrokeWidth(mValueWidth);
mOutlinePaint.setStyle(Style.STROKE);
mOutlinePaint.setStrokeWidth(mBorderWidth);
mOutlinePaint.setColor(Color.WHITE);
mRect.set(mHalfXSubRadius, mHalfYSubRadius, mHalfXAddRadius, mHalfYAddRadius);
canvas.drawArc(mRect, ARC_START-1, MAX_ARC+2, false, mOutlinePaint);
canvas.drawArc(mRect, ARC_START, MAX_ARC, false, mFillPaint);
U shouldnt make your arc bigger, instead try to draw the same sized arc (in white), X pixel right,down,up,left,corners as well (total of 8 drawings).
where X is the border size u want.
after that draw the main arc (in gray) in the middle.
psuedo code:
paint=white;
drawArc(x,y+2);
drawArc(x,y-2);
drawArc(x+2,y+2);
drawArc(x+2,y-2);
drawArc(x-2,y+2);
drawArc(x-2,y+2);
drawArc(x+2,y);
drawArc(x-2,y);
paint=gray;
drawArc(x,y);
My question may be a bit unclear, but I have extended the View class and generated a number of shapes on the canvas around (0,0). I want to put this point in the middle, so I have to tell the View that it has to draw horizontally, for example, from -640 to 640 on the x-axis and vertically, for example, from -360 to 360 on the y-axis.
Is there a way to tell the view that it has to draw these pixels without changing the coordinates of the drawn shapes. I just want to tell the view that it has to draw certain coordinates.
I want to be able to change dynamically which area is drawn.
I'm not 100% what you are trying to achieve, but if you want to move and scale your shapes, you can use the canvas translate or scale methods, to move the canvas to under your shapes. Remember that it is the canvas you translate, and not the shape, so the transformations will have to be done in reverse. You should also use the canvas save and restore methods to restore your canvas position between transformations.
If you instead want to limit any drawing to an area, you can use the canvas clip-methods, for example:
canvas.clipRect(-640, -360, 640, 360);
Would case any drawing outside that rectangle to be discarded.
i have a rotated arc drawn using android 2d graphics
c.drawArc(new RectF(50, 50, 250, 250), 30, 270, true, paint);
the arc will rotate while the game is running ,
i wanna know how i can detect if any other game objects(rects ,circles) collide with it ??
this is the first time for me to write a game :)
i saw something like this in http://hakim.se/experiments/html5/core/01/
Thanks in advance
Arc collisions are slightly harder then normal collisions, but using boolean algebra you can easily check if a given point is inside your arc.
Take a look at the following picture.
There are 3 objects here. The black sphere, this visualizes your arc, if something collides with it, it might be inside your arc. The red sphere on top of the black sphere, this visualizes the 'inside' of the arc, if something is inside the red sphere, it's definately not 'inside' the arc. Now there is also the green triangle that Visualizes the 'cut-off' of your arc, anything inside the green triangle is also definately not in your arc.
Testing if something is inside the black sphere is easy. (object's distance to center of sphere <= radius of sphere). Same for the red sphere. The green triangle is a bit tricky, you first have to construct this. Find the start and end radians of your arc. and rotate a unit vector by start radians. Then rotate a unit vector by end radians. Lengthen both these vectors by 2 * the radius of the black sphere. Now use the center point of your arc and the positions of two vectors with added the center position as the 3 points of the triangle. You can then use one of the point-triangle collision solvers: http://www.bing.com/search?q=point+triangle+collision&go=&form=QBLH&scope=web
So remember: collision with arc = (collision with black sphere) && !(collision with red sphere) && !(collision with green triangle).