Issue creating borders with draw() / drawPolygon() in Graphics2D - java

More adventures with Graphics2D!
This time I'm investigating the various paint / colour modes and ways to paint complex borders. The typical method is to fill the Shape or Polygon, and then draw the same Shape over the top (in a different colour). I'm familiar with setting Strokes and so forth to vary border thickness and other bits.
My latest attempt was to use RadialGradientPaint as a way of defining multiple bands of colour, but this doesn't appear to apply to the draw / drawPolygon call (or the extra colours aren't showing up.
The draw call is drawing a thickness 3 empty Polygon, but as a uniformly-blue strip. Below is the gradient / paintComponent code:
g2d.setStroke(new BasicStroke(h.getHexBorderWidth())); // this is set to 3
Point2D center = new Point2D.Float(50, 50);
float radius = 1;
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
// the float value is defined as 0.0 in the constructor
RadialGradientPaint p =
new RadialGradientPaint(center, radius, new Point2D.Float(), dist, colors,
CycleMethod.NO_CYCLE);
g2d.setPaint(p);
if(((BasicHexagon) h).isAnimating()
&& ((BasicHexagon) h).getCurrentTransform() != null) { // just general null checks
g2d.draw(((BasicHexagon) h).getCurrentTransform()); // this is a transformed Shape
} else {
g2d.drawPolygon(h.getBase()); // this is a Polygon
}
Any better ways I'm missing out on, or any way that this isn't being called correctly?

Related

Java Graphics 2d avoid Polyline distorted corners

i'm working on a graphic interface for drawing subway maps. A line is represented with station as circles and a polyline to link them.You can move the stations with a mouseDrag and of course it updates the displaying map in real time. My problem is when stations comes to a certain angle, there is a polyline distortion and the corner created by the 2 lines is out of the station circle display, i'd like to know if there is a way to avoid this.
screenshots of the app with the polyline issue
here's my code for the polyline's draw
//x and y point array creation
xPoints = new int[this.stationViews.size()];
yPoints = new int[this.stationViews.size()];
for (int i=0;i<this.stationViews.size();i++) {
//fill arrays with the center point of circles representing stations
xPoints[i] = this.stationViews.get(i).getStation().getPosX()-this.stationViews.size()/2;
yPoints[i] = this.stationViews.get(i).getStation().getPosY()-this.stationViews.size();
}
//setting color
g2D.setColor(this.line.getColor());
//set stroke width relative to the zoom level
int strokeWidth=5;
if(!this.stationViews.isEmpty()) {
if (this.stationViews.get(0).getStationSize()>14) {
strokeWidth = this.stationViews.get(0).getStationSize()-13;
}else {
strokeWidth = 3;
}
}
g2D.setStroke(new BasicStroke(strokeWidth));
//draw the polyline
if (this.stationViews.size() >1) {
g2D.drawPolyline(xPoints, yPoints, this.stationViews.size());
}
//draw the station (g2D.drawCircle)
for (StationView stationView : stationViews) {
stationView.affiche(g2D,this.line.getColor());
}
thank you for your help
That is called the miter. You seem to be per default using JOIN_MITER, sharp joining of extended lines at the end, which can point far out of the join for small angles.
g2d.setStroke(new BasicStroke(strokeWidth,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 5));
miter a surface forming the beveled end or edge of a piece where a joint is made by cutting two pieces at an angle and fitting them together.
It is also a bishop's cap with a pointy top, hence the name.

Android draw one arc within another arc

I am trying to draw an arc within another arc at at distance of 10px in Wear OS.
We know that an arc can be drawn using the following:
canvas.drawArc(arcBounds, 0, 90, false, paint);
The above statement will draw an arc from 3pm to 6pm.
I would like to draw another arc at the same startAngle and sweepAngle within the above arc.
Assuming that the total number of arc that need to be drawn is based on the size of a list, how can I achieve the expected output?
Here is what my original arcBounds init method looks like:
private void initArcBounds(int width, int height, int additionalMargin) {
float margin = res.getDimension(R.dimen.timearc_margin) + additionalMargin;
arcBounds.left = margin;
arcBounds.top = margin;
arcBounds.right = width - margin;
arcBounds.bottom = height - margin;
}
for (Event el : eventList) {
canvas.drawArc(arcBounds, start, sweepAngle, false, timeArcPaints);
arcBounds.inset(10,10);
}
To create concentric arcs inward, we just need to "shrink" the bounds by the desired measure on each side. The RectF class has the convenient inset(dx, dy) method which will do just that. It expects those measures in pixels, so we can simply pass 10 for each, with no adjustments needed.
This will modify the RectF's bounds, so to keep those at their initial values in the original, we can create a temporary copy before the draw loop with RectF's copy constructor.
For example, altogether:
RectF temp = new RectF(arcBounds);
for (...) {
canvas.drawArc(temp, 0, 90, false, paint);
temp.inset(10f, 10f);
}

How to fade out the edges of a Java2D Area object?

I have a method that creates shadows based on where things are on the screen and the final product of that method is an Area, which I then draw onto the screen and it contains all shadows on screen in the same Area. This is because the drawn shadows are a low opacity so if it is not all one thing they will overlap and the opacity will make it look weird.
The issue that I want the shadows to look like they fade out, but I have absolutely no idea how, or if that would even be possible. Is there a way to soften the edges of the Area or make them gradient fade out? I tried to do graphics2D.setPaint(new GradientPaint[a gradient effect]) but it did nothing.
Thanks in advance
EDIT: Here is a screenshot of the program making a shadow around a 'building' rectangle. The green rectangle is to show the effect of the shadow. The end result I want is instead of the shadow abruptly ending, it should fade out.
i guess you have problems setting up the proper colors for the gradient:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(30, 30, 50, 20);
Graphics2D g2d = (Graphics2D) g;
Polygon p = new Polygon(new int[]{10,20,50,50,40,10}, new int[]{10,10,40,50,50,20}, 6);
Area a = new Area(p);
Color b = new Color(0xFF000000, true); //using colors with transparency!!!
Color w = new Color(0x00FFFFFF, true); //using colors with transparency!!!
//try to use proper values for the x/y values - both, start and end
GradientPaint gradient = new GradientPaint(0, 0, b, 40, 40, w);
g2d.setPaint(gradient);
g2d.fill(a); // fill your area!
}
result is a gradient with alpha (Transparency) ...
Area drawn in red <==> Area drawn with Gradient
don't forget to set the alpha value 0xAARRGGBB to FF for 100% opaque (0xFF000000 is black 100%opaque) to 0 (0x00FFFFFF white, 100% transparent)

Drawing filled polygon with libGDX

I want to draw some (filled) polygons with libGDX. It shoudn't be filled with a graphic/texture. I have only the vertices of the polygon (closed path) and tried to visualize with meshes but at some point this is not the best solution, I think.
My code for an rectangle is:
private Mesh mesh;
#Override
public void create() {
if (mesh == null) {
mesh = new Mesh(
true, 4, 0,
new VertexAttribute(Usage.Position, 3, "a_position")
);
mesh.setVertices(new float[] {
-0.5f, -0.5f, 0
0.5f, -0.5f, 0,
-0.5f, 0.5f, 0,
0.5f, 0.5f, 0
});
}
}
// ...
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
mesh.render(GL10.GL_TRIANGLE_STRIP, 0, 4);
}
is there a function or something to draw filled polygons in an easier way?
Since recent updates of LibGDX, #Rus answer is using deprecated functions. However, I give him/her credits for the new updated version below:
PolygonSprite poly;
PolygonSpriteBatch polyBatch = new PolygonSpriteBatch(); // To assign at the beginning
Texture textureSolid;
// Creating the color filling (but textures would work the same way)
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0xDEADBEFF); // DE is red, AD is green and BE is blue.
pix.fill();
textureSolid = new Texture(pix);
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] { // Four vertices
0, 0, // Vertex 0 3--2
100, 0, // Vertex 1 | /|
100, 100, // Vertex 2 |/ |
0, 100 // Vertex 3 0--1
}, new short[] {
0, 1, 2, // Two triangles using vertex indices.
0, 2, 3 // Take care of the counter-clockwise direction.
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
For good triangulating algorithms if your polygon is not convex, see the almost-linear earclipping algorithm from Toussaint (1991)
Efficient triangulation of simple polygons, Godfried Toussaint, 1991
Here is a libGDX example which draws a 2D concave polygon.
Define class members for PolygonSprite PolygonSpriteBatch
PolygonSprite poly;
PolygonSpriteBatch polyBatch;
Texture textureSolid;
Create instances, 1x1 size texture used with red pixel as workaround. An array of coordinates (x, y) is used for initialization of the polygon.
ctor() {
textureSolid = makeTextureBox(1, 0xFFFF0000, 0, 0);
float a = 100;
float b = 100;
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] {
a*0, b*0,
a*0, b*2,
a*3, b*2,
a*3, b*0,
a*2, b*0,
a*2, b*1,
a*1, b*1,
a*1, b*0,
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
}
Draw and rotate polygon
void draw() {
super.draw();
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
poly.rotate(1.1f);
}
I believe the ShapeRenderer class now has a polygon method for vertex defined polygons:
ShapeRenderer.polygon()
You can use the ShapeRenderer API to draw simple, solid-color shapes with Libgdx.
The code you've given is a reasonable way to draw solid color polygons too. Its much more flexible than ShapeRenderer, but is a good bit more complicated. You'll need to use glColor4f to set the color, or add a Usage.Color attribute to each vertex. See the SubMeshColorTest example for more details on the first approach and the MeshColorTexture example for details on the second approach.
Another option to think about is using sprite textures. If you're only interested in simple solid colors objects, you can use very simple 1x1 textures of a single color and let the system stretch that across the sprite. Much of Libgdx and the underlying hardware are really optimized for rendering textures, so you may find it easier to use even if you're not really taking advantage of the texture contents. (You can even use a 1x1 white texture, and then use a SpriteBatch with setColor and draw()
to draw different color rectangles easily.)
You can also mix and match the various approaches, too.
Use triangulation algorithm and then draw all triangles as GL_TRIANGLE_STRIP
http://www.personal.psu.edu/cxc11/AERSP560/DELAUNEY/13_Two_algorithms_Delauney.pdf
just wanted to share my related solution with you, namely for implementing and drawing a walkZone with scene2d. I basically had to put together the different suggestions of the others' posts:
1) The WalkZone:
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Polygon;
import com.mygdx.game.MyGame;
public class WalkZone extends Polygon {
private PolygonRegion polygonRegion = null;
public WalkZone(float[] vertices) {
super(vertices);
if (MyGame.DEBUG) {
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0x00FF00AA);
pix.fill();
polygonRegion = new PolygonRegion(new TextureRegion(new Texture(pix)),
vertices, new EarClippingTriangulator().computeTriangles(vertices).toArray());
}
}
public PolygonRegion getPolygonRegion() {
return polygonRegion;
}
}
2) The Screen:
you can then add a listener in the desired Stage:
myStage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if (walkZone.contains(x, y)) player.walkTo(x, y);
// or even directly: player.addAction(moveTo ...
return super.touchDown(event, x, y, pointer, button);
}
});
3) The implementation:
The array passed to te WZ constructor is a set of x,y,x,y... points. If you put them counter-clockwise, it works (I didn't check the other way, nor know how it exactly works); for example this generates a 100x100 square:
yourScreen.walkZone = new WalkZone(new int[]{0, 0, 100, 0, 100, 100, 0, 100});
In my project it works like a charm, even with very intricated polygons. Hope it helps!!
Most answers suggest triangulation, which is fine, but you can also do it using the stencil buffer. It handles both convex and concave polygons. This may be a better solution if your polygon changes a lot, since otherwise you'd have to do triangulation every frame. Also, this solution properly handles self intersecting polygons, which EarClippingTriangulator does not.
FloatArray vertices = ... // The polygon x,y pairs.
Color color = ... // The color to draw the polygon.
ShapeRenderer shapes = ...
ImmediateModeRenderer renderer = shapes.getRenderer();
Gdx.gl.glClearStencil(0);
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
Gdx.gl.glColorMask(false, false, false, false);
renderer.begin(shapes.getProjectionMatrix(), GL20.GL_TRIANGLE_FAN);
renderer.vertex(vertices.get(0), vertices.get(1), 0);
for (int i = 2, n = vertices.size; i < n; i += 2)
renderer.vertex(vertices.get(i), vertices.get(i + 1), 0);
renderer.end();
Gdx.gl.glColorMask(true, true, true, true);
Gdx.gl.glStencilOp(GL20.GL_ZERO, GL20.GL_ZERO, GL20.GL_ZERO);
Gdx.gl.glStencilFunc(GL20.GL_EQUAL, 1, 1);
Gdx.gl.glEnable(GL20.GL_BLEND);
shapes.setColor(color);
shapes.begin(ShapeType.Filled);
shapes.rect(-9999999, -9999999, 9999999 * 2, 9999999 * 2);
shapes.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
To use the stencil buffer, you must specify the number of bits for the stencil buffer when your app starts. For example, here is how to do that using the LWJGL2 backend:
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.stencil = 8;
new LwjglApplication(new YourApp(), config);
For more information on this technique, try one of these links:
http://commaexcess.com/articles/7/concave-polygon-triangulation-shortcut
http://glprogramming.com/red/chapter14.html#name13
http://what-when-how.com/opengl-programming-guide/drawing-filled-concave-polygons-using-the-stencil-buffer-opengl-programming/

Image not drawn to a fixed position when rotating with AffineTransform

I have a problem with rotating image in a fixed position with Graphcis2D and AffineTransform.
The idea is to rotate an image according to body's rotation.
The rotation is happening correctly as the rotation angle of the image matches the angle of the body's rotation. However, as the rotation takes place, the image is not drawn to the same position as the body should be drawn. The code of the method painting the picture is the following:
public void paintPicture(Graphics g, Body body) {
Graphics2D g2 = (Graphics2D) g;
Vector2f[] vertices = ((Box) body.getShape()).getPoints(body.getPosition(), body.getRotation());
Vector2f topLeftCorner = vertices[0];
AffineTransform oldTransform = g2.getTransform();
AffineTransform at = new AffineTransform();
at.rotate(body.getRotation());
g2.setTransform(at);
g2.drawImage(this.img, (int) topLeftCorner.x, (int) topLeftCorner.y, null);
g2.setTransform(oldTransform);
}
Any ideas what might cause the movement of the image instead of drawing it according to the coordinates (topLeftCorner.x, topLeftCorner.y)?
You need to first translate you object so the anchor point (the point around which you want it to rotate) is at the origin, perform your rotation, then translate it back. So if you want to rotate around the point (50, 75), you'd do the following:
at.translate (-50, -75);
at.rotate (body.getRotation());
at.translate (50, 75);
I'm assuming that your AffineTransform class can accumulate transformations. If not, you'll need 3 different transforms.

Categories