I know how to apply sprite to a Box2d body, but is there a way to apply a texture to it? Basically what I am trying to do is to have one texture, let's say 32x32, and then just repeat it all over the body, like the ground in this image:
Is this possible in LibGDX?
EDIT:
My latest try:
Fixture fixture = body.createFixture(fixtureDef);
Vector2 mTmp = new Vector2();
PolygonShape shape = (PolygonShape) fixture.getShape();
int vertexCount = shape.getVertexCount();
float[] vertices = new float[vertexCount * 2];
for (int k = 0; k < vertexCount; k++) {
shape.getVertex(k, mTmp);
mTmp.rotate(body.getAngle()* MathUtils.radiansToDegrees);
mTmp.add(body.getPosition());
vertices[k * 2] = mTmp.x * PIXELS_PER_METER;
vertices[k * 2 + 1] = mTmp.y * PIXELS_PER_METER;
}
short triangles[] = new EarClippingTriangulator().computeTriangles(vertices).toArray();
Texture texture = new Texture(Gdx.files.internal("data/block.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion textureRegion = new TextureRegion(texture, 0, 0, texture.getWidth(), texture.getHeight());
PolygonRegion region = new PolygonRegion(textureRegion, vertices, triangles);
poly = new PolygonSprite(region);
and in rendering:
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
but it doesn't draw anything.
After importing different shape of level, I get this result:
Only one polygon ( shown inside of red circle ) gets the texture. Whole level is imported as a JSON file
Yes This is very much possible in libgdx.
You just need to create a polygon region for that
PolygonRegion region = new PolygonRegion(textureRegion, vertices, triangles);
Here textureRegion is the region that you want to repeat.
vertices and triangles define the shape of the region.
This polygon region is a repeated texture that is form red from the vertices and triangles. You can render this region using polygon batch just same as we do it with sprite batch.
UPDATE
PolygonShape shape = (PolygonShape) fixture.getShape();
int vertexCount = shape.getVertexCount();
float[] vertices = new float[vertexCount * 2];
for (int k = 0; k < vertexCount; k++) {
shape.getVertex(k, mTmp);
mTmp.rotate(body.getAngle()* MathUtils.radiansToDegrees);
mTmp.add(bodyPos);
vertices[k * 2] = mTmp.x * PIXELS_PER_METER;
vertices[k * 2 + 1] = mTmp.y * PIXELS_PER_METER;
}
short triangles[] = new EarClippingTriangulator()
.computeTriangles(vertices)
.toArray();
PolygonRegion region = new PolygonRegion(
textureRegion, vertices, triangles);
Related
I want to place some objects (ModelInstance) on the floor (also a ModelInstance) of my game world. To get the position for these objects, I let a Ray intersect the floor. The point of intersection should then be the required position.
My plan is to set the origin of the ray below the floor, so that the direction of the ray goes straight up and hits the floor from below. Both ModelInstances are .g3db Models made in Blender.
Vector3 dir = new Vector3(0, 10, 0); //Vector points upwards
Ray ray = new Ray(new Vector3(), dir.cpy());
Mesh mesh = landscape.model.meshes.first(); //The floor ModelInstance, has only a single mesh
int fac = mesh.getVertexSize();
float[] verts = new float[mesh.getNumVertices() * fac];
short[] inds = new short[mesh.getNumIndices()];
mesh.getVertices(verts);
mesh.getIndices(inds);
for (int j = 0; j < 10; j++) { //add 10 objects to the floor
Vector3 out = new Vector3(- 15, -50f, - j * 5);
ray.origin.set(out.cpy()); //set the origin of the vector below the floor
if (Intersector.intersectRayTriangles(ray, verts, inds, fac, out)) {
System.out.println(j + " out = " + out); //out should be the position for my objects
}
}
The output of the intersectRayTriangles Method is exactly the initial position below the floor. But this point is not anywhere close to the floor. How do I get the proper point of intersection?
I finally found a (semi optimal) solution which works.
landscape is a ModelInstance, created with Blender.
ArrayList<Vector3> vertices = new ArrayList<>();
landscape.calculateTransforms();
Renderable rend = new Renderable();
Mesh mesh = landscape.getRenderable(rend).meshPart.mesh;
int vertexSize = mesh.getVertexSize() / 4;
float[] verts = new float[mesh.getNumVertices() * vertexSize];
short[] inds = new short[mesh.getNumIndices()];
mesh.getVertices(verts);
mesh.getIndices(inds);
for (int i = 0; i < inds.length; i++) {
int i1 = inds[i] * vertexSize;
Vector3 v = new Vector3(verts[i1], verts[i1 + 1], verts[i1 + 2]);
v.set(v.prj(rend.worldTransform));
vertices.add(v);
}
Vector3 dir = new Vector3(0, 10, 0);
Vector3 pos = new Vector3(random.nextFloat(),random.nextFloat(),random.nextFloat());
Ray ray = new Ray(pos, dir.cpy());
for (int i = 0; i < vertices.size() - 3; i+=3){
if (Intersector.intersectRayTriangle(ray, vertices.get(i), vertices.get(i + 1), vertices.get(i + 2), pos)) {
//pos now contains the correct coordinates
break;
}
}
Note that the y-Axis faces upwards
I try to merge multiple meshes with a transformation matrix into a single mesh.
Each mesh has 4 data sets.
Vertices
Indices
Texture Coordinates
Normals
The way I'm trying to do it is supposed to be lazy and not cost that much CPU.
It is a 3 step process.
Multiply each vertex and normal with the transformation matrix.
Merge the Vertices, Texture Coordinates and Normals of each mesh into 3 big arrays.
Merge the Indices of each mesh into a single array but use the sum of the previous meshes as an offset. For example: If mesh 1 has 800 indices then 800 has to be added to all of the indices from mesh 2.
This method has two big problems.
Duplicate vertices are not shared
Parts that are invisible due to clipping are not removed
But that is OK as this is supposed to be a lazy method with not much CPU usage. It is already optimal for creating meshes for grass and bushes.
I have attempted an implementation of this method which looks like this:
public static final MeshData mergeLazy(List<MeshData> meshes, List<Matrix4f> transformations) {
int lengthVertices = 0;
int lengthNormals = 0;
int lengthTexCoords = 0;
int lengthIndices = 0;
ArrayList<Integer> indexLengths = new ArrayList<>();
for(MeshData mesh : meshes) {
lengthVertices += mesh.getVertices().length;
lengthNormals += mesh.getNormals().length;
lengthTexCoords += mesh.getTextureCoordinates().length;
int length = mesh.getIndices().length;
lengthIndices += length;
indexLengths.add(length);
}
float[] vertices = new float[lengthVertices];
float[] texCoords = new float[lengthTexCoords];
float[] normals = new float[lengthNormals];
int[] indices = new int[lengthIndices];
int iv = 0;
int ivt = 0;
int ivn = 0;
int i = 0;
int indexLength = 0;
for(int im = 0; im < meshes.size(); im++) {
MeshData mesh = meshes.get(im);
float[] mVertices = mesh.getVertices();
float[] mTexCoords = mesh.getTextureCoordinates();
float[] mNormals = mesh.getNormals();
int[] mIndices = mesh.getIndices();
Matrix4f transformation = transformations.get(im);
for(int index = 0; index < mVertices.length; index += 3) {
Vector3f vertex = MatrixUtil.multiply(transformation, mVertices[index], mVertices[index + 1], mVertices[index + 2]);
vertices[iv++] = vertex.x;
vertices[iv++] = vertex.y;
vertices[iv++] = vertex.z;
Vector3f normal = MatrixUtil.multiply(transformation, mNormals[index], mNormals[index + 1], mNormals[index + 2]);
normals[ivn++] = normal.x;
normals[ivn++] = normal.y;
normals[ivn++] = normal.z;
}
for(int index = 0; index < mTexCoords.length; index++) {
texCoords[ivt++] = mTexCoords[index];
}
for(int index = 0; index < mIndices.length; index++) {
indices[i++] = indexLength + mIndices[index];
}
indexLength += indexLengths.get(im);
}
MeshData data = new MeshData();
data.setIndices(indices);
data.setNormals(normals);
data.setTextureCoordinates(texCoords);
data.setVertices(vertices);
return data;
}
In the end I actually have a single mesh and the multiplying of the transformation also works.... for rotation and scaling, but here come the problems.
The multiplying with the transformation does NOT work for the translation.
My method for multiplying a matrix with a vector looks like this:
public static final Vector3f multiply(Matrix4f matrix, float x, float y, float z) {
Vector3f result = new Vector3f();
result.x = x * matrix.m00 + y * matrix.m01 + z * matrix.m02;
result.y = x * matrix.m10 + y * matrix.m11 + z * matrix.m12;
result.z = x * matrix.m20 + y * matrix.m21 + z * matrix.m22;
return result;
}
And the second problem is that the textures of the second mesh are somewaht off.
Here is a picture:
As you can see the second mesh only has about 1/4 of the actual texture.
The code I used to generate this mesh looks like this:
Material grassMaterial = new Material();
grassMaterial.setMinBrightness(0.1F);
grassMaterial.setColorMap(new Texture(new XImgTextureReader().read(new FileInputStream("res/textures/grass2.ximg"))));
grassMaterial.setAffectedByLight(true);
grassMaterial.setTransparent(true);
grassMaterial.setUpwardsNormals(true);
grassMaterial.setFog(fog);
MeshData quad = Quad.generateMeshData(
new Vector3f(0.0F, 1F, 0.0F),
new Vector3f(0.0F, 0.0F, 0.0F),
new Vector3f(1F, 0.0F, 0.0F),
new Vector3f(1F, 1F, 0.0F)
);
StaticMesh grassMesh = new StaticMesh(MeshUtil.mergeLazy(Arrays.asList(quad, quad), Arrays.asList(
MatrixUtil.createTransformationMatrx(
new Vector3f(0.0F, 0.0F, 0.0F),
new Vector3f(0.0F, 0.0F, 0.0F),
new Vector3f(1.0F, 1.0F, 1.0F)
),
MatrixUtil.createTransformationMatrx(
new Vector3f(0F, 0.0F, -0F),
new Vector3f(0.0F, 90.0F, 0.0F),
new Vector3f(1.0F, 1.0F, 1.0F)
)
)));
grassMesh.setCullMode(StaticMesh.CULLING_DISABLED);
Entity grass = new Entity();
grass.setShaderPipeline(shaderPipeline);
grass.setMaterial(grassMaterial);
grass.setMesh(grassMesh);
grass.setTranslation(0, 0, 1);
My question now is: What did I do wrong? Why is the texture so weird and why does the multiplication with the transformation not work for the translation?
If you need more of the code, I have a GitHub Repo with the Eclipse Project here: https://github.com/RalleYTN/Heroica-Fabulis
Thanks to #Rabbid76 I came closer to my answer and now I have finally found the problem.
The first problem with the translation not working was fixed by multiplying the transformation vertically instead of horizontally. Thanks again #Rabidd76 .
And the reason why the textures where so weird is because I merged the indices incorrectly. I should not have taken the sum of all indices in the meshes before as offset but the sum of the vertices.
Here is now the working method:
public static final MeshData mergeLazy(List<MeshData> meshes, List<Matrix4f> transformations) {
ArrayList<Float> vertices = new ArrayList<>();
ArrayList<Float> texCoords = new ArrayList<>();
ArrayList<Float> normals = new ArrayList<>();
ArrayList<Integer> indices = new ArrayList<>();
int offset = 0;
int m = 0;
for(MeshData mesh : meshes) {
Matrix4f transformation = transformations.get(m);
float[] mVertices = mesh.getVertices();
float[] mNormals = mesh.getNormals();
for(int index = 0; index < mesh.getVertices().length; index += 3) {
Vector3f vertex = MatrixUtil.multiply(transformation, mVertices[index], mVertices[index + 1], mVertices[index + 2]);
vertices.add(vertex.x);
vertices.add(vertex.y);
vertices.add(vertex.z);
Vector3f normal = MatrixUtil.multiply(transformation, mNormals[index], mNormals[index + 1], mNormals[index + 2]);
normals.add(normal.x);
normals.add(normal.y);
normals.add(normal.z);
}
ListUtil.addFloatArray(texCoords, mesh.getTextureCoordinates());
int[] mIndices = mesh.getIndices();
for(int index : mIndices) {
indices.add(index + offset);
}
offset += mVertices.length / 3;
m++;
}
MeshData mesh = new MeshData();
mesh.setIndices(ListUtil.toPrimitiveIntArray(indices));
mesh.setNormals(ListUtil.toPrimitiveFloatArray(normals));
mesh.setTextureCoordinates(ListUtil.toPrimitiveFloatArray(texCoords));
mesh.setVertices(ListUtil.toPrimitiveFloatArray(vertices));
return mesh;
}
I'm trying to create a Kaleidoscope with an ImageView in Android. I'm struggling to get the rotation and mirroring for each 'segment' correct. I'm new to image manipulation and I'm trying to adapt the code example from here for android.
I have the following code:
private void drawKaleidoscope() {
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.cropped_landscape);
Bitmap imageview_bitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Bitmap matrix_bitmap;
BitmapShader fillShader;
Path triangle_mask = new Path();
RectF r = new RectF(0, 0, bm.getWidth(), bm.getHeight()); // create new rectangle to match the dimensions of our image
this.radius = (int)r.height() / 2;
Canvas c = new Canvas(imageview_bitmap);
c.drawColor(Color.BLACK);
float start_angle = 0;
for (int i = 0; i < this.segments; i++) {
// Create pie-slice shape mask
triangle_mask.reset();
triangle_mask.moveTo(r.centerX(), r.centerY());
triangle_mask.arcTo(r, start_angle, angle);
triangle_mask.close();
// Use odd even check to decide when to mirror the image or not
if (i % 2 == 0) {
Matrix mat = new Matrix();
mat.preTranslate(-radius, -radius);
mat.postRotate(i * angle);
mat.postTranslate(radius, radius);
matrix_bitmap = Bitmap.createBitmap(bm, 0, 0, (int)r.width(), (int)r.height(), mat, true);
}
else {
Matrix mat = new Matrix();
// mirror on x axis
mat.postScale(-1, 1);
mat.postTranslate(-radius, radius);
mat.postRotate((float)-Math.PI);
mat.postRotate(i * angle);
mat.postTranslate(radius, -radius);
matrix_bitmap = Bitmap.createBitmap(bm, 0, 0, (int)r.width(), (int)r.height(), mat, true);
}
fillShader = new BitmapShader(matrix_bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
// Fill the triangle masked area with our image now
Paint fill = new Paint();
fill.setColor(0xFFFFFFFF);
fill.setStyle(Paint.Style.FILL);
fill.setShader(fillShader);
c.drawPath(triangle_mask, fill);
start_angle += angle;
}
kal.setImageBitmap(imageview_bitmap);
}
The output of the above function looks like so:
If anyone could provide some insight on how to properly do the image rotations/mirroring that would be greatly appreciated.
Okay I ended up going about this a different way. Instead of rotating the source image I simply draw the image mask to the same spot on the canvas, and then rotate the canvas itself. Let's say I have 12 image 'slices'. I draw 6 alternating segments, flip the canvas via canvas.scale(-1, 1) and then draw another 6 segments in where the blank spaces are. Here's the code I ended up with:
private Bitmap generateKaleidoscopeBitmap(float start_angle) {
Canvas canvas = new Canvas(imageview_bitmap);
canvas.drawColor(Color.BLACK);
BitmapShader fillShader;
Path triangle_mask = new Path();
RectF r = new RectF(0, 0, imageview_bitmap.getWidth(), imageview_bitmap.getHeight()); // create new rectangle to match the dimensions of our image
int centerX = imageview_bitmap.getWidth() / 2;
int centerY = imageview_bitmap.getHeight() / 2;
// how much to rotate the canvas by after the image is flipped
float offset = calculateCanvasSymmetryOffset(start_angle);
// Create a pie-slice shaped clipping mask
triangle_mask.moveTo(r.centerX(), r.centerY());
triangle_mask.arcTo(r, start_angle, angle);
triangle_mask.close();
// Fill the triangle masked area with our shader now
Paint fill = new Paint();
fill.setColor(0xFFFFFFFF);
fill.setStyle(Paint.Style.FILL);
fillShader = new BitmapShader(source_image, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
fill.setShader(fillShader);
// Rotate the canvas and draw the clipping mask to the canvas
for (int i = 0; i < this.segments / 2; i++) {
canvas.drawPath(triangle_mask, fill);
canvas.rotate(angle * 2, centerX, centerY);
}
// mirror the canvas and rotate it once to counter the symmetrical offset
canvas.scale(-1, 1, centerX, centerY);
canvas.rotate(offset, centerX, centerY);
// Rotate the now mirrored canvas and draw the clipping mask to it
// This is a cheap and easy way of creating mirrored segments
for (int i = 0; i < this.segments / 2; i++) {
canvas.drawPath(triangle_mask, fill);
canvas.rotate(angle * 2, centerX, centerY);
}
return imageview_bitmap;
}
I'm using Libgdx's port of box2d. I'm creating an edge shape, but it does not rotate upon collision with other polygons. Why is this?
Here's my code for setting up the edge shape.
float x = 3;
float y = 10;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x, y);
Body body = this.world.createBody(bodyDef);
MassData massdata = new MassData();
massdata.center.set(1,1);
massdata.mass = 10;
body.setMassData(massdata);
FixtureDef fixtureDef = new FixtureDef();
EdgeShape shape = new EdgeShape();
shape.set(0, 0, 2, 2);
fixtureDef.shape= shape;
fixtureDef.friction = 1;
fixtureDef.restitution = .5f;
fixtureDef.density = 10;
body.createFixture(fixtureDef);
That is because an Edge Shape in Box2D is meant to create static scenery. It simply cannot move due to dynamics. I guess kinematic would work. This is logic, because an EdgeShape has no mass, since it has no surface (or volume if you would consider the 2D world as 3D with a fixed depth).
Here is an excerpt taken from the manual, which confirms my initial thoughts:
Edge shapes are line segments. These are provided to assist in making
a free-form static environment for your game. A major limitation of
edge shapes is that they can collide with circles and polygons but not
with themselves. The collision algorithms used by Box2D require that
at least one of two colliding shapes have volume. Edge shapes have no
volume, so edge-edge collision is not possible.
As Martijn said, I calculate and set mass data of the body. To do this, I calculate the center of mass assuming uniform density. I am using LIBGDX and so I needed a way to do this in java. I wrote the routine using Box2D's source. Feel free to use it for whatever you like.
EdgeShapes do not have mass, and so do not behave physically. To cause this kind of behavior, manually calculation was necessary.
public static MassData calculateMassData(List<Vector2> vertices,
float density) {
float k_inv3 = 1.0f / 3.0f;
float area = 0.0f;
float I = 0.0f;
Vector2 center = new Vector2();
Vector2 s = new Vector2();
for (int i = 0, size = vertices.size(); i < size; i++) {
s.add(vertices.get(i));
}
s.div(vertices.size());
for (int i = 0, size = vertices.size(); i < size; ++i) {
// Triangle vertices.
Vector2 e1 = new Vector2(vertices.get(i));
e1.sub(s);
Vector2 e2 = new Vector2(i + 1 < size ? vertices.get(i + 1)
: vertices.get(0));
e2.sub(s);
float D = e1.crs(e2);
float triangleArea = 0.5f * D;
area += triangleArea;
// Area weighted centroid
center.add(e1.cpy().add(e2).mul(k_inv3).mul(triangleArea));
float ex1 = e1.x, ey1 = e1.y;
float ex2 = e2.x, ey2 = e2.y;
float intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
float inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
I += (0.25f * k_inv3 * D) * (intx2 + inty2);
}
MassData data = new MassData();
// Total mass
data.mass = density * area;
center.mul(1.0f / area);
data.center.set(center.cpy().add(s));
// Inertia tensor relative to the local origin (point s).
data.I = density * I;
// Shift to center of mass then to original body origin.
data.I += data.mass
* (data.center.dot(data.center) - center.dot(center));
data.I = (data.I);
return data;
}
2 limitations of edge shapes:
Edge shapes do not collide with other edge shapes
Edge shapes are not normal polygons. There's no real outside/inside definitions for any polygons built using only edge shapes.
Because of #2, other bodies can wind up inside of polygons built with edge shapes.
Because of #1, I took a different approach and constructed my polygon using a series of narrow rectangles. This is working well, but unfortunately does not solve #2.
I am not wanting to use PolygonShape for fixtures because the polygon I am attempting to model is complex. It can have hundreds of vertices.
I have tried triangulating my polygon and creating fixtures from each of these triangles. Unfortunately often the resulting triangle's area is too small for Box2D, and causes exceptions with box2d.
I am open to other solutions here.
Try: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Body.html#setFixedRotation%28boolean%29
body.setFixedRotation(false)
Also your friction is too high. Try 0.6f
I created a program to draw many polygons automatically everytimes user presses a button. The points of the polygon are generated automatically using the random function. The problem is that, since the points of the polygon were randomly generated, some of the polygon are overlap with other polygon. How can I avoid this, so that every polygon shown without being overlapped?
.....
List<Polygon> triangles = new LinkedList<Polygon>();
Random generator = new Random();
public void paintComponent(Graphics g) {
for(int i = 0; i < 10; i++) {
double xWidth = generator.nextDouble() * 40.0 + 10.0;
double yHeight = generator.nextDouble() * 40.0 + 10.0;
xCoord[0] = generator.nextInt(MAX_WIDTH);
yCoord[0] = generator.nextInt(MAX_HEIGHT);
xCoord[1] = (int) (xCoord[0] - xWidth);
xCoord[2] = (int) (xCoord[1] + (xWidth/2));
yCoord[1] = yCoord[0];
yCoord[2] = (int) (yCoord[1] - yHeight);
triangles.add( new Polygon(xCoord,yCoord, 3));
}
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.00f));
g2.setPaint(Color.black);//set the polygon line
for (Polygon triangle : triangles) g2.drawPolygon(triangle);
Polygon[] triArray = triangles.toArray(new Polygon[triangles.size()]);
for (Polygon p:triArray) triangles.remove (p);
}
Check out the game programming wiki on Polygon Collision:
http://gpwiki.org/index.php/Polygon_Collision
You could break your canvas into 10 regions and constrain your polygons each to their own region. To do this, you could use your i value and a %100 (or other suitable magnitude) of your randomly generated value and apply them to your x coordinates and y coordinates as applicable. The result would be a grid of similarly constrained(no larger than the grid cell), but randomly shaped, Polygons.
EDIT:
Taking another look and fooling around a bit, I took the general concept as I described above and made a stab at an implementation:
public void paintComponent(Graphics g) {
int[] xCoord = new int[3];
int[] yCoord = new int[3];
int colCnt = 5;
int rowCnt = 2;
int maxCellWidth = getWidth() / colCnt;
int maxCellHeight = getHeight() / rowCnt;
for (int i = 0; i < (colCnt * rowCnt); i++) {
int xMultiple = i % colCnt;
int yMultiple = i / colCnt;
for (int j = 0; j < 3; j++) {
xCoord[j] = generator.nextInt(maxCellWidth)
+ (maxCellWidth * xMultiple);
yCoord[j] = generator.nextInt(maxCellHeight)
+ (maxCellHeight * yMultiple);
}
triangles.add(new Polygon(xCoord, yCoord, 3));
}
//... the rest of your method
}
As you can see, all of the Polygons have all points randomly generated, as opposed to your method of generating the first point and then making the rest relative to the first. There is a sense of randomness that is lost, however, as the Polygons are laid out in a grid-like pattern.
Create Area objects from your new polygon as well as for all existing polygons.
Subtract the new polygon's area from the existing ones. If the subtract changed the area, the polygons overlap.
Area newArea = new Area(newPolygon);
Area existingArea = new Area(existingPolygon);
Area existingAreaSub = new Area(existingPolygon); existingAreaSub.subtract(newArea);
boolean intersects = existingAreaSub.equals(existingArea);
You could implement a method Polycon.containsPoint( x, y ) and repeat your random generation until this method returns false for all drawn Polygons.
I have achieved this in Android Using Kotlin (See github project) by using JTS see here
Step-1:
Add JTS library to your project
implementation group: 'org.locationtech.jts', name: 'jts-core', version: '1.15.0'
Step-2:
Create JTS polygon objects for both polygon
// create polygons One
var polygoneOneArray: ArrayList<Coordinate> = ArrayList()
for (points in polygonOnePointsList) {
polygoneOneArray.add(Coordinate(points.latitude(), points.longitude()))
}
val polygonOne: org.locationtech.jts.geom.Polygon = GeometryFactory().createPolygon(
polygoneOneArray.toTypedArray()
)
// create polygons Two
var polygoneTwoArray: ArrayList<Coordinate> = ArrayList()
for (points in polygoneTwoPointsList) {
polygoneTwoArray.add(Coordinate(points.latitude(), points.longitude()))
}
val polygonTwo: org.locationtech.jts.geom.Polygon = GeometryFactory().createPolygon(
polygoneTwo.toTypedArray()
)
Step-3:
Get Common Area of both Polygon
val intersection: org.locationtech.jts.geom.Geometry = polygonOne.intersection(polygonTwo)
Step-4:
Remove common Area from polygonTwo
val difference: org.locationtech.jts.geom.Geometry = polygonTwo.difference(intersection)
Step-5:
Merge Both polygonOne and update polygonTwo
val union: org.locationtech.jts.geom.Geometry = mergePolygonList.get(0).polygons.union(difference)
Step-5:
Now pick points from Geometry and draw a final merged Polygon
val array: ArrayList<Coordinate> = union.coordinates.toList() as ArrayList<Coordinate>
val pointList: ArrayList<Point> = ArrayList()
for (item in array) {
pointList.add(Point.fromLngLat(item.y, item.x))
}
var list: ArrayList<List<Point>> = ArrayList<List<Point>>()
list.add(pointList)
style.addSource(
GeoJsonSource(
"source-id${timeStamp}",
Feature.fromGeometry(Polygon.fromLngLats(list))
)
)