How to merge 2D rectangles into larger polygons [Java] - java

I have a 2D polygon that only has lines that are perfectly horizontal or perfectly verticle. There are 2 other posts relating to this. However, one is merging multiple rectangles at once into a polygon, and the other is written in javascript.
I would like to take a rectangle of any size and add it to an existing 2D Polygon. Some examples are below.
In the diagram, each box represents 1 pixel. The navy boxes are the points that data exists for and the red and green boxes show how these points are connected. The points of both the red polygon and the green rectangle are held in a linked list (the order of points is what determines what point connects to what). Red and Green boxes don't exist in memory, they're used here only to show connections.
A point is defined as having an x and y coordinate.
How would I go about merging the green rectangle into the red polygon?
I really appreciate any help given thanks!
Edit: Here is some code that draws these shapes:
public class Core
{
private static Polygon polygon = new Polygon();
private static Rectangle rect = new Rectangle();
public static void main(String args[])
{
new Core();
}
public Core()
{
polygon = new Polygon(new int[] {0,12,12,24,24,32,32,12,12,5,5,0},new int[] {0,0,4,4,-3,-3,13,13,15,15,8,8},12);
rect = new Rectangle(18,14,7,6);
Draw2D drawing = new Draw2D();
drawing.setVisible(true);
}
public class Draw2D extends JFrame
{
private static final long serialVersionUID = -1033542824627245527L;
public Draw2D()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(new DrawPane(), BorderLayout.CENTER);
pack();
}
}
class DrawPane extends JComponent
{
private static final long serialVersionUID = 5457330297413941626L;
public DrawPane() {
setPreferredSize(new Dimension(1280,720));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.black);
g2.clearRect(0,0,1280,720);
g2.translate(getWidth() / 2, getHeight() / 2);
g2.setColor(Color.white);
g2.setStroke(new BasicStroke(0.001f));
g2.draw(polygon);
g2.draw(rect);
}
}
}
Edit 2:
A partial solution that I have is to inflate the rectangle by pushing each side out by 1 pixel/box, then checking if the entire side is inside one of the red polygon lines. By inside I mean collinear. A green rectangle side is sitting inside one of the red polygon sides.
If it does sit inside then leave the points there, if not move them back to where they originally were. Repeat this clockwise until all 4 sides have ha their points moved or not. Duplicate point objects that reference the same point as a point object that already exists will be deleted so that only 1 point object references 1 point.
This is only part of a solution as this method doesn't work when a rectangle is added on the edge of a polygon (shown in the first image, 2nd and 3rd examples from the top down).

You can simplify your problem by separating it in different parts. So the issue is based on lines.
Horizontal case
1.
R1------R2
xxxxxx (overlaps)
G1------------G2
2.
R1----------R2
xxxxxxx (overlaps)
G1------------G2
3.
R1---------------R2
no overlapping
G1-----G2
...
distance: abs(R_y1 - G_y1)
range of overlap: x1 = max(R_x1,G_x1), x2 = min(R_x2,G_x2),
if (distance <= 1 && x1-x2 > 0) {
// both lines are neighbours and has a valid overlapping range
}
Vertical case
Analogous to horizontal
As your boxes are stored in linked lists, you iterate both boxes and calculate distance and range of overlap.
List<Line> greenBox; // {Line((28,30),(60,30)) , Line((60,30),(60,50)), ...
List<Line> redBox; // ...
for (Line r : redBox) {
for (Line g : greenBox) {
if (distance(r,g) <= 1 && rangeOfOverlap(r,g) > 0) {
// both lines matches and can be merged over the range of overlaps
merge(r,g);
}
}
}
This is just an example to inspire you. You'll need to check if the picked green and red line are both horizontal or both vertical.
The complexity of the algorhythm would be around at m^n (without merging) where n is the number of green lines and m is the number of red lines.
lol

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.

Polygon and line intersecting with intersectLinePolygon not behaving properly

I'm trying to detect a collision between a small rectangle around the cursor and a "Connector", which is basically just a line between two points.
Now, I've decided to use the Intersector.intersectLinePolygon(p1, p2, polygon) method to do so, but when I run the code. It detects a collision everytime any of the rectangle X or Y points are in the same range as the line's bounding box and I can't really get my head around it. The desired result is the collision reporting only when the rectangle is actually touching the line.
Vector3 worldPos = cam.unproject(new Vector3(mouseX, mouseY, 0));
Rectangle rect = new Rectangle(worldPos.x-4, worldPos.y-4, 8, 8);
Boolean connectorIntersected = false;
for (int i = 0; i < nodeConnectorHandler.getAllConnectors().size(); i++) {
//Getting two points that make the connector line
Node n1 = nodeConnectorHandler.getAllConnectors().get(i).getFrom();
Node n2 = nodeConnectorHandler.getAllConnectors().get(i).getTo();
float x1 = n1.getCX();
float y1 = n1.getCY();
float x2 = n2.getCX();
float y2 = n2.getCY();
//Making a polygon out of rect
Polygon p = new Polygon(new float[] {
rect.getX(),
rect.getY(),
(rect.getX()+8f),
rect.getY(),
(rect.getX()+8f),
(rect.getY()+8f),
rect.getX(),
(rect.getY()+8f)
});
//Checking if the line intersects the polygon (representing the rectangle around the cursor)
if (Intersector.intersectLinePolygon(new Vector2(x1,y1), new Vector2(x2,y2), p))
{
selectedIndex = nodeConnectorHandler.getAllConnectors().get(i).getID();
System.out.println("ConnectorIntersected!");
connectorIntersected = true;
}
break
}
The code reports a collision everytime the rectangle is in these areas (shown in yellow, aprox):
photoshopped image link
The red line inbetween those 2 dots is the "connector"
Cursor is right below the line. It reports a collision in those yellow areas spanning across the whole game world.
I suppose I'm either not using the function properly or that I've made some obvious mistake. Or is this how the function should react? I really don't know. Thanks for any help :)
Ok, apparently I used the wrong method. intersectSegmentPolygon works as expected. My bad ¯_(ツ)_/¯.

libGdx collision detection Polygons

I started learning LibGdx and Java recently, and it has been going well so far.
I'm facing an issue with collision detection.
I have two sprites which can be represented as two shapes, a polygon and a circle, which will collide/intersect at any given moment. Once these two shapes collide, something will get triggered.
So far, this is what I have done. It kinda works but it is not accurate. This is called inside the Render() function:
public boolean CollectPowerUp(PowerUps powerUp) {
if (powerUp.position.dst(position) < Constants.PLAYER_HEIGHT -3) {
Gdx.app.log("Collected PowerUp", "TRUE");
EnablePowerUp(powerUp);
return true;
}
return false;
I have searched many websites, and most of the solutions include other softwares like 2DCube or PhysicsEditor. Is it possible to perform this intersection solely by using LibGdx and Java? If so, what should I look into?
Thanks
Intersector class having many static method that can be used for collision detection.
If your polygon is rectangle you can use :
Intersector.overlaps(Circle c, Rectangle r)
else
Polygon polygon=new Polygon();
polygon.setVertices(new float[]{0,0,.......});
Circle circle=new Circle(x, y, radius);
float points[]=polygon.getTransformedVertices();
for (int i=0;i<points.length;i+=2){
if(circle.contains(points[i],points[i+1])){
System.out.println("Collide");
}
}
EDIT
Above code only detect collision if polygon vertices are inside circle, what if
circle is completely inside polygon
some part of circle is inside polygon but vertices are outside the circle
Create a polygon for circle that act as circle in view and polygon in model
float radius=100;
FloatArray floatArray=new FloatArray();
int accuracy=24; // can be use 1 for complete circle
for (int angle=0;angle<360;angle += accuracy){
floatArray.add(radius * MathUtils.cosDeg(angle));
floatArray.add(radius * MathUtils.sinDeg(angle));
}
Polygon circle=new Polygon(floatArray.toArray()); // This is polygon whose vertices are on circumference of circle
float[] circularPoint=circle.getTransformedVertices();
for (int i=0;i<circularPoint.length;i+=2){
if(polygon.contains(circularPoint[i],circularPoint[i+1])){
System.out.println("Collide With circumference");
break;
}
}
There's a nice article on collision detection on www.gamedevelopment.blog which shows how to detect collisions with most shapes. This is the Libgdx circle, polygon collision detection method shown in the article.
public boolean contains (Polygon poly, Circle circ) {
final float[] vertices = poly.getTransformedVertices(); // get all points for this polygon (x and y)
final int numFloats = vertices.length; // get the amount of points(x and y)
// loop through each point's x and y values
for (int i = 0; i < numFloats; i += 2) {
// get the first and second point(x and y of first vertice)
Vector2 start = new Vector2(vertices[i],vertices[i + 1]);
// get 3rd and 4th point (x and y of second vertice) (uses modulo so last point can use first point as end)
Vector2 end = new Vector2(vertices[(i + 2) % numFloats], vertices[(i + 3) % numFloats]);
// get the center of the circle
Vector2 center = new Vector2(circ.x, circ.y);
// get the square radius
float squareRadius = circ.radius * circ.radius;
// use square radius to check if the given line segment intersects the given circle.
return Intersector.intersectSegmentCircle (start, end, center, squareRadius);
}
}
There are many useful methods in the Intersector class which can be used for collision detection.

Draw points as ovals using java swing

I have a point with [x, y] coordinate, for example
Point p = new Point(1, 2). I want to visualize it. However, I want this point to an oval, like in the picture below.
How can I do that using java Swing?
Determine the size of the oval you want, for example 10 px. Then draw the oval with the x and y points starting at the Point (x and y), minus half the size. Something like:
public class PointsPanel extends JPanel {
List<Point> points = new ArrayList<Point>();
int size = 10;
...
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point p : points) {
int x = p.x - size/2;
int y = p.y - size/2;
g.drawOval(x, y, size, size);
}
}
}
For more general details on painting, see Performing Custom Painting. Also see the Graphics API for other drawXxx and fillXxx methods.
From the image you posted, what you need is a graph layout library like JUNG(not sure about current status of this project). You can follow peeskillet's answer but need to take care of problems like overlapping ovals etc.

User-selected marker in time series data in Java

My code plots 5000 points of time series data in a panel that is 581 pixels wide by default, but this width changes when the user resizes the window. My code also plots several rectangular markers that each identify a local maximum/peak in this same space.
I need to enable the user to right click on any of the rectangular-peak-markers so that the user can manually delete any false peak. The problem is that my code is reporting different x-coordinates than expected when the user right-clicks on a peak-marker. I suspect that the reason may have to do with rounding error in converting from 581 x-pixels back to 5000 data indices. But I am not certain of the reason.
Can anyone suggest a solution that enables my users to manually select one of the above-described peak markers by right-clicking on it?
I am enclosing relevant sections of the code below. My actual code is very, very long, and too complicated to post. But the relevant portions below should be enough for someone to see the logic of my approach, and to then suggest a more effective approach.
The code that declares the class in question is:
class SineDraw extends JPanel implements MouseMotionListener, MouseListener {
// lots of code, including the two segments excerpted below
}
This segment of code overloads the paintComponent of the JPanel so that my data is plotted:
// declare some variables
ArrayList<Double> PeakList = new ArrayList<Double>() // this ArrayList is populated by an extraneous process
visiblePoints = 5000
hstep = getWidth()/visiblePoints //=581/5000 by default, but will change when user resizes window
int numPeaks = PeakList.size();
// scale (y-coordinate) data relative to height of panel
pts = new double[visiblePoints]
for (int i = 0; i < pts.length-1; i++){pts[i]=//data vertical scaled to fill panel;}
// plot the 5000 time-series-data-points within the 581 pixels in x-axis
for (int i = 1; i < visiblePoints; i++) {
int x1 = (int) ((i - 1) * hstep);
int x2 = (int) (i * hstep);
int y1 = (int)pts[i - 1];
int y2 = (int)pts[i];
g2.drawLine(x1, y1, x2, y2);
}
// plot a rectangle for each of the local peaks
for(int m=0;m<=(numPeaks-1);m++){
if(i==(int)(PeakList.get(m)){
int currentVal = (int)pts[(int)(PeakList.get(m)];
g2.drawRect((int)(PeakList.get(m), currentVal, 6, 6);
}
}
This section of code is for handling the right-clicking of the mouse:
public void mousePressed(MouseEvent e){
// check to see if right mouse button was clicked
boolean jones = (e.getModifiers()&InputEvent.BUTTON3_MASK)==InputEvent.BUTTON3_MASK;
if(jones==true){
// test the value returned as x-coordinate when user right-clicks (code always underestimates x-coordinate of local peaks by this test)
double ReverseHstep = visiblePoints/getWidth();
int getX_ConvertedTo_i = (int) (e.getX()*ReverseHstep);
System.out.println("getX_ConvertedTo_i is: "+getX_ConvertedTo_i );
// check to see if peaklist contains a value within the x-coordinates of the user-selected-rectangle
if(PeakList.contains((double)(e.getX()-3))
||PeakList.contains((double)(e.getX()-2))
||PeakList.contains((double)(e.getX()-1))
||PeakList.contains((double)(e.getX()))
||PeakList.contains((double)(e.getX()+1))
||PeakList.contains((double)(e.getX()+2))
||PeakList.contains((double)(e.getX()+3))
){
// handling code will go here, but for now it is a print test that never succeeds because x-coordinate is always underestimated
System.out.println("You just selected a peak!");
}
}
repaint();
}
I suggest you create objects (in this case Rectangles) for each thing you want to be clickable. Here is an over-simplified example of how you can make something you draw clickable. The key thing to take away from this is the mouseClicked method which will display a dialog only if the mouse clicked within the rectangle.
One tricky point is that I wasn't able to figure out how to make the rectangle filled in with color without drawing another rectangle over it. I'll leave that one for you ;-)
public class Canvas extends JPanel implements MouseListener{
private Rectangle rect = new Rectangle(100,100);
public Canvas(){
this.addMouseListener(this);
rect.setSize(100, 100);
}
#Override
public void paintComponent(Graphics g){
g.setClip(rect);
g.setColor(Color.RED);
g.fillRect(0, 0, 100, 100);
}
#Override
public void mouseClicked(MouseEvent e){
if(rect.contains(e.getPoint())){
JOptionPane.showConfirmDialog(this, "Click!");
}
}
// The rest of the MouseListener methods have been cut out
public static void main(String[] a){
JFrame frame = new JFrame("Canvas Thingy");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(0, 0, 300, 300);
frame.add(new Canvas());
frame.setVisible(true);
}
}

Categories