How to avoid overlapping polygon - java

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))
)
)

Related

Java geotools how to create coverage grid

How to create grid coverage when each cell is 5M ?
I found this :
GridCoverage2D coverage = reader.read(null);
// direct access
DirectPosition position = new DirectPosition2D(crs, x, y);
double[] sample = (double[]) coverage.evaluate(position); // assume double
// resample with the same array
sample = coverage.evaluate(position, sample);
Source : https://docs.geotools.org/latest/userguide/library/coverage/grid.html
I didn't found a lot of tutorial about how to create grid coverage on geotools...
To create an empty coverage you need to use the GridCoverageFactory and one of the create methods. Since you are not constructing from an existing image you need to provide some memory for your raster to be stored in (this can also hold any initial values you want). For this your choices are a float[][] or a WritableRaster. Finally, you need a Envelope to say where the coverage is and what it's resolution is (otherwise it is just an array of numbers), I favour using a ReferencedEnvelope so that I know what the units are etc, so in the example below I have used EPSG:27700 which is the OSGB national grid so I know that it is in metres and I can define the origin somewhere in the South Downs. By specifying the lower left X and Y coordinates and the upper right X and Y as resolution times the width and height (plus the lower left corner) the maths all works out to make sure that the size of my pixels is resolution.
So keeping it simple for now you could do something like:
float[][] data;
int width = 100;
int height = 200;
data = new float[width][height];
int resolution = 5;
for(int i=0;i<width;i++){
for(int j=0;j<height;j++ ){
data[i][j] = 0.0f;
}
}
GridCoverageFactory gcf = new GridCoverageFactory();
CoordinateReferenceSystem crs = CRS.decode("EPSG:27700");
int llx = 500000;
int lly = 105000;
ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(llx, llx + (width * resolution), lly, lly + (height * resolution),
crs);
GridCoverage2D gc = gcf.create("name", data, referencedEnvelope);
If you want more bands in your coverage then you need to use a WriteableRaster as the base for your coverage.
WritableRaster raster2 = RasterFactory.createBandedRaster(java.awt.image.DataBuffer.TYPE_INT, width,
height, 3, null);
for (int i = 0; i < width; i++) {//width...
for (int j = 0; j < height; j++) {
raster2.setSample(i, j, 0, rn.nextInt(200));
raster2.setSample(i, j, 1, rn.nextInt(200));
raster2.setSample(i, j, 2, rn.nextInt(200));
}
}

How do I create an Area object using a PathIterator?

I am clearly missing an important concept here. I have written code using mouse events to draw a boundary (a polygon) on an existing BufferedImage. Here is the relevant section:
public void paintComponent(Graphics g)
{
super.paintComponent(g); //Paint parent's background
//G3 displays the BufferedImage "Drawing" with each paint
Graphics2D G3 = (Graphics2D)g;
G3.drawImage(this.Drawing, 0, 0, null);
G3.dispose();
}
public void updateDrawing()
{
int x0, y0, x1, y1; // Vertex coordinates
Line2D.Float seg;
// grafix is painting the mouse drawing to the BufferedImage "Drawing"
if(this.pts.size() > 0)
{
for(int ip = 0; ip < pts.size(); ip++)
{
x0 = (int)this.pts.get(ip).x;
y0 = (int)this.pts.get(ip).y;
this.grafix.drawRect(x0 - this.sqw/2, y0 - this.sqh/2, + this.sqw, this.sqh);
if (ip > 0)
{
x1 = (int)this.pts.get(ip-1).x;
y1 = (int)this.pts.get(ip-1).y;
this.grafix.drawLine(x1, y1, x0, y0);
seg = new Line2D.Float(x1, y1, x0, y0);
this.segments.add(seg);
}
}
}
repaint();
}
The next two routines are called by the mouse events: Left click gets the next point and right click closes the region.
public void getNextPoint(Point2D p)
{
this.isDrawing = true;
Point2D.Float next = new Point2D.Float();
next.x = (float) p.getX();
next.y = (float) p.getY();
this.pts.add(next);
updateDrawing();
}
public void closeBoundary()
{
//Connects the last point to the first point to close the loop
Point2D.Float next = new Point2D.Float(this.pts.get(0).x, this.pts.get(0).y);
this.pts.add(next);
this.isDrawing = false;
updateDrawing();
}
It all works fine and I can save the image with my drawing on it:
image with drawing
The list of vertices (pts) and the line segments (segments) are all that describe the region/shape/polygon.
I wish to extract from the original image only that region enclosed within the boundary. That is, I plan to create a new BufferedImage by moving through all of the pixels, testing to see if they fall within the figure and keep them if they do.
So I want to create an AREA from the points and segments I've collected in drawing the shape. Everything says: create an AREA variable and "getPathIterator". But on what shape? My AREA variable will be empty. How does the path iterator access the points in my list?
I've been all over the literature and this website as well.
I'm missing something.
Thank you haraldK for your suggestion. Before I saw your post, I came to a similar conclusion:
Using the Arraylist of vertices from the paint operation, I populated a "Path2D.Float" object called "contour" by looping through the points list that was created during the "painting" operation. Using this "contour" object, I instantiated an Area called "interferogram". Just to check my work, I created another PathIterator, "PI", from the Area and decomposed the Area, "interferogram" into "segments" sending the results to the console. I show the code below:
private void mnuitmKeepInsideActionPerformed(java.awt.event.ActionEvent evt)
{
// Keeps the inner area of interest
// Vertices is the "pts" list from Class MouseDrawing (mask)
// It is already a closed path
ArrayList<Point2D.Float> vertices =
new ArrayList<>(this.mask.getVertices());
this.contour = new Path2D.Float(Path2D.WIND_NON_ZERO);
// Read the vertices into the Path2D variable "contour"
this.contour.moveTo((float)vertices.get(0).getX(),
(float)vertices.get(0).getY()); //Starting location
for(int ivertex = 1; ivertex < vertices.size(); ivertex++)
{
this.contour.lineTo((float)vertices.get(ivertex).getX(),
(float)vertices.get(ivertex).getY());
}
this.interferogram = new Area(this.contour);
PathIterator PI = this.interferogram.getPathIterator(null);
//Test print out the segment types and vertices for debug
float[] p = new float[6];
int icount = 0;
while( !PI.isDone())
{
int type = PI.currentSegment(p);
System.out.print(icount);
System.out.print(" Type " + type);
System.out.print(" X " + p[0]);
System.out.println(" Y " + p[1]);
icount++;
PI.next();
}
BufferedImage masked = Mask(this.image_out, this.interferogram);
// Write image to file for debug
String dir;
dir = System.getProperty("user.dir");
dir = dir + "\\00masked.png";
writeImage(masked, dir, "PNG");
}
Next, I applied the mask to the image testing each pixel for inclusion in the area using the code below:
public BufferedImage Mask(BufferedImage BIM, Area area)
{
/** Loop through the pixels in the image and test each one for inclusion
* within the area.
* Change the colors of those outside
**/
Point2D p = new Point2D.Double(0,0);
// rgb should be white
int rgb = (255 << 24);
for (int row = 0; row < BIM.getWidth(); row++)
{
for (int col = 0; col < BIM.getHeight(); col++)
{
p.setLocation(col, row);
if(!area.contains(p))
{
BIM.setRGB(col, row, rgb);
}
}
}
return BIM;
}
public static BufferedImage deepCopy(BufferedImage B2M)
{
ColorModel cm = B2M.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = B2M.copyData(B2M.getRaster()
.createCompatibleWritableRaster());
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
This worked beautifully (I was surprised!) except for one slight detail: the lines of the area appeared around the outside of the masked image.
In order to remedy this, I copied the original (resized) image before the painting operation. Many thanks to user1050755 (Nov 2014) for the routine deepCopy that I found on this website. Applying my mask to the copied image resulted in the portion of the original image I wanted without the mask lines. The result is shown in the attached picture. I am stoked!
masked image

Catmull Rom Spline implementation (LibGDX)

I want to generate a random spline across my screen.
Here is what I have so far:
public class CurvedPath {
Random rn;
CatmullRomSpline<Vector2> curve;
float[] xPts;
float[] yPts;
Vector2[] points;
public CurvedPath(){
points = new Vector2[10];
rn = new Random();
curve = new CatmullRomSpline<Vector2>(points,false);
for(int i = 0 ; i < 10; i++){
xPts[i] = rn.nextFloat()*SampleGame.WIDTH;
yPts[i] = SampleGame.HEIGHT*i/10;
}
}
}
I'm pretty confused on the documentation that has been provided on how to use the CatmullRomSpline object ( https://github.com/libgdx/libgdx/wiki/Path-interface-&-Splines )
Basically what I am trying to do here is generate 10 random points equally distributed across the height of my screen, and randomly placed along the width of the screen to create a randomized curved path.
So within the constructor's for loop you can see that I generate the x and y values of each control point for the spline.
How can I give input these points into the spline object and render it on the screen?
-thanks
update
Let me reword my question to be a little more specific..
I have my control points represented by xPts and yPts. Now I want to get the points that fall along the spline, how do I do this using these two vectors? The constructor for a CatmullRomSpline takes a Vector2, not two float[] 's
what you did. Fill with points:
curve = new CatmullRomSpline<Vector2>(points,false);
To get a point on the curve:
Vector2 point = new Vector2();
curve.valueAt(point, 0.5f);
valueAt() Parameter explanation:
1 (point) the point you are looking for is stored in the Vector2 object.
float between 0 and 1, 0 is the first point, 1 the last one. 0.5f is middle. This float represents the hole distance from first to last point.
Getting and render 100 points can look like this:
Vector2 point = new Vector2();
for (int i = 0; i <= 100; i++) {
curve.valueAt(point, i * 0.01f);
// draw using point.x and point.y
}
answer to you edited question:
for(int i = 0 ; i < 10; i++){
points[i].x = rn.nextFloat()*SampleGame.WIDTH;
points[i].y = SampleGame.HEIGHT*i/10;
}
curve = new CatmullRomSpline<Vector2>(points,false);
The process is also detailed here: https://github.com/libgdx/libgdx/wiki/Path-interface-&-Splines
/*members*/
int k = 100; //increase k for more fidelity to the spline
Vector2[] points = new Vector2[k];
/*init()*/
CatmullRomSpline<Vector2> myCatmull = new CatmullRomSpline<Vector2>(dataSet, true);
for(int i = 0; i < k; ++i)
{
points[i] = new Vector2();
myCatmull.valueAt(points[i], ((float)i)/((float)k-1));
}

Java Path2D.Double, Append But No Subtract

I have looked into using the EVEN-ODD Winding rule in order to create holes in an area. The thing is on the forum all I could come up with was a Path2D.Double() appending a hole within itself. I want to create a whole in an already existing Path2D much like the Area class has the Area.subtract(new Shape()); does. I am desperate. I know Path2D has the ability to improve the performance of my game.
Path2D.Double d = new Path2D.Double(Path2D.WIND_EVEN_ODD);// this is a much larger circle which I will be using to subtract from the tile...
d.append(current_draw_area.createTransformedArea(mouse_point), false);//Note that this is an area without a winding rule...
Path2D.Double shape= new Path2D.Double();//this is actually a empty tile which is simply a rectangle, but will eventually have something in it to subtract from...
shape.append(d, false);
Note: Output should be either an empty square because the much larger circle covers the tile completely or a tile that has the result of the intersection of the circle and existing tile shape removed leaving what was left of the tile...
Research Link
StackOverflow Thread
EDIT:
for (int i = 0; i < paint_textures.size(); i++) {
if (!current_paint.equals(paint_textures.get(i))) {
paint_regions.get(paint_textures.get(i)).subtract(new Area(current_draw_area.createTransformedArea(mouse_point)));
for (int y = adjY - 100; y < adjY + current_draw_area.getBounds().height + 100; y += 100) {
for (int x = adjX - 100; x < adjX + current_draw_area.getBounds().width + 100; x += 100) {
try {
if (paint_tiles.get(paint_textures.get(i)).get(new Point(x, y)).intersects(new Rectangle(x, y, 100, 100))) {
Area a = (Area) paint_regions.get(paint_textures.get(i)).clone();
a.intersect(new Area(new Rectangle(x, y, 100, 100)));
Path2D.Double d = new Path2D.Double(a.getPathIterator(null).getWindingRule());
d.append(a.getPathIterator(null), false);//This Causes painting to be Jumpy...
paint_tiles.get(paint_textures.get(i)).replace(new Point(x, y), d);
}
} catch (NullPointerException e) {
}
}
}
}
}

Drawing polygons on right position

this question is continue of GPS coordinates to pixel.
I need to draw a several polygons. I can draw each polygon alone, but cant all polygons on right position.
I load information about polygon from this file:
GPS
I have class Kraj, which represent each polygon.
public class Kraj {
String name;
Point2D.Double points[];
Point2D.Double transPoints[];
Point2D.Double max;
Point2D.Double min;
// polygon
Path2D.Double polygon;
ArrayList<Kraj> kraje;
public Kraj(String name, Point2D.Double body[])
{
this.name = name;
this.body = Arrays.copyOf(body, body.length);
// calculate a bounding box
zjistiLimity();
this.transPoints = new Point2D.Double[points.length];
}
private void transformToWindow(int width, int height)
{
// convert to window
double convertX = width / (max.x - min.x);
double convertY = height / (max.y - min.y);
// calculate polygon to fit in window with right aspect ratio
double convert = convertX > convertY ? convertY : convertX;
// min = 0, convert to interval <0: infinity> and multiply by convert,
for (int j = 0; j < points.length; j++) {
double transX = (points[j].x - min.x) * convert;
double transY = height - (points[j].y - min.y) * convert;
transPoints[j] = new Point2D.Double(transX, transY);
}
this.polygon = new Path2D.Double();
this.polygon.moveTo(transBody[0].x, transBody[0].y);
for (int i = 1; i < body.length; i++)
this.polygon.lineTo(transPoints[i].x, transPoints[i].y);
this.polygon.closePath();
}
private void drawKraj(Graphics2D g2, int width, int height) {
g2.setStroke(new BasicStroke(2));
g2.fill(polygon);
// vykreslime obrys
g2.setColor(Color.black);
g2.draw(polygon);
}
public void draw(Graphics2D g2,
int contextWidth, int contextHeight)
{
// fit to window size
int sirkaSOdsazenim = contextWidth;
int vyskaSOdsazenim = contextHeight;
this.transformujToWindow(sirkaSOdsazenim, vyskaSOdsazenim);
this.drawKraj(g2, sirkaSOdsazenim, vyskaSOdsazenim);
}
/**
* Set min and max
*/
private void zjistiLimity() {
max = new Point2D.Double(-Double.MAX_VALUE, -Double.MAX_VALUE);
min = new Point2D.Double(Double.MAX_VALUE, Double.MAX_VALUE);
for(int j = 0; j < 10; j++)
{
for (int i = 0; i < body.length; i++)
{
if (points[i].getX() < min.getX()) min.x = points[i].getX();
if (points[i].getY() < min.getY()) min.y = points[i].getY();
if (points[i].getX() > max.getX())max.x = points[i].getX();
if (points[i].getY() > max.getY()) max.y = points[i].getY();
}
}
}
With this code I can draw polygon, which fit to window. But I need to draw all polygons to fit to window (calculate coordinates to create this map):
What I need to edit or add? Thanks for all answers.
You can translate the entire polygon by using:
g2.translate(x, y);
g2.draw(polygon);
g2.translate(-x, -y)
Determining the appropriate x/y translation for each polygon is something you will need to do.
I need to draw a several polygons. I can draw each polygon alone, but I can't draw all polygons in the right position.
If you can draw each polygon alone, then your polygons are correct.
You need to add an origin point to your Kraj class. Then your draw method would transform the polygon points from the polygon origin to the map origin. Assuming your polygon origin is (10,10) and a particular polygon needs to be drawn at (20,30), then you would add 10 to the x and add 20 to the y of each point in the polygon before you draw it.
You can do this my making a copy of the polygon in the draw routine before you adjust the X and Y values of each point.
Edited to add: Here's your own code modified to transform the origin. I've not tested these changes.
private void transformToWindow(Point2D windowOrigin, int width, int height)
{
// convert to window
double convertX = width / (max.x - min.x);
double convertY = height / (max.y - min.y);
// calculate polygon to fit in window with right aspect ratio
double convert = convertX > convertY ? convertY : convertX;
// min = 0, convert to interval <0: infinity> and multiply by convert,
for (int j = 0; j < points.length; j++) {
double transX = (points[j].x - min.x) * convert;
double transY = height - (points[j].y - min.y) * convert;
transPoints[j] = new Point2D.Double(transX, transY);
}
this.polygon = new Path2D.Double();
double xShift = windowOrigin.x - transBody[0].x;
double yShift = windowOrigin.y - transBody[0].y;
this.polygon.moveTo(windowOrigin.x, windowOrigin.y);
for (int i = 1; i < body.length; i++)
this.polygon.lineTo(transPoints[i].x + xShift,
transPoints[i].y + yShift);
this.polygon.closePath();
}

Categories