How to draw circle on JavaFX chart - java

I develop a JavaFX application and I am struggling with solving this problem:
I have an array of points (x, y) that I am plotting over a scatter chart, you can see in the picture below they appear as a small gray points.
Those points I'd like to enclose with the smallest circle, so for that I have a function called minimalCircle that receives an array of points and while using the Welzl's algorithm it returns the minimal enclosing circle (radius + center point)
Circle c = minimalCircle(points);
The problem occurs when I am trying to plot the circle I retrieve from the function, I make a series containing 1 point which is the center of the circle, and applying a circle shape with the radius of the circle
private XYChart.Series<Number, Number> minimalCircleSeries(Point[] points) {
XYChart.Series series = new XYChart.Series();
Circle c = minimalCircle(points);
javafx.scene.shape.Circle pointCircle = new javafx.scene.shape.Circle(c.center.x, c.center.y, c.r);
XYChart.Data data = new XYChart.Data(c.center.x, c.center.y);
data.setNode(pointCircle);
series.getData().add(data);
return series;
}
Unfortunately when it draws the circle it refers the radius size by pixels while its actually suppose to be real units on the graph, and that cause the circle in the picture below to not be the smallest enclosing circle.
I am trying to find a way to convert the radius scale so the circle will be plotted correctly or to find another way to plot a circle over a scatter chart
Thank you very much for your help !
Example image of the chart

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.

How to draw a circle on a map according to its real radius in kilometers (Using UnfoldingMaps and Processing in Java)

Earthquake threat circle on the map
I am using UnfoldingMaps to display earthquake information on the map.
I plan to show the threat circle on the map.
A circle is drawn given its radius and center position in pixels. How to get the radius is the problem I met.
Suppose I have the threat circle radius R in kilometers and the center marker A.
I want to create a marker B on the circle so that I can use the screen distance as the screen radius.
I decided to create B with the same longitude but a different latitude from A. I change R to delta latitude.
But after drawing the circle I found it is not the right one since the red triangular should be in the circle according to their distance.
The main difficulty is exactly how to calculate screen radius according to kilometers.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
float deltaLat=(float) (threatCircle()/6371/2/3.1415927*360);
Location centerLocation = this.getLocation();
Location upperLocation = new Location(centerLocation);
upperLocation.setLat(centerLocation.getLat() + deltaLat);
SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = this.getScreenPosition(map);
ScreenPosition upper = upperMarker.getScreenPosition(map);
radius = Math.abs(upper.y - center.y);
setThreatCircleOnScreen(radius);
}
This is going to depend on two things: the zoom level of the map, and the projection you're using.
You need to unproject kilometers to pixels, and you can probably figure out how to do that using google and the Unfolding API.
For example, I found a MercatorProjection class that contains a constructor that takes a zoom level, and methods for projecting and unprojecting points between world coordinates and pixel coordinates.
That's just a starting point, since I'm not sure what units those methods are taking, but hopefully this is a direction for you to take your googling and experimenting.
I'd recommend trying to get something working and posting an MCVE if you get stuck. Good luck.
Now I have the answer for this question. Hope it will be helpful for others.
Earthquake threat circle on the map
My early solution to calculate radius in pixels from km is correct. I think it a simple and powerful idea (independent of projecting API)
The only problem is I should use diameter rather than radius in drawing the circle. I should draw with d=2r like this
float d = 2 * threatCircleRadius();
pg.noFill();
pg.ellipse(x,y,d,d);
I found another cleaner solution like below by consulting the author of UnfoldingMaps. (https://github.com/tillnagel/unfolding/issues/124)
My early solution first changes distance to delta latitude, then create new location by changing latitude.
The new solution use the API GeoUtils.getDestinationLocation(sourceLocation, compassBearingDegree, distanceKm) to directly get the new location!
In addition, I needn't create a new marker to find its screen position.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
Location centerLocation = this.getLocation();
Location upperLocation = GeoUtils.getDestinationLocation(centerLocation, 0, threatCircle());
//SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = map.getScreenPosition(centerLocation);
ScreenPosition upper = map.getScreenPosition(upperLocation);
radius = PApplet.dist(center.x, center.y, upper.x, upper.y);
setThreatCircleOnScreen(radius);
}

Bounding box doesn't follow sprite correctly. (Java)

In a Java 2D game, I have a rectangular sprite of a tank. The sprite can rotate in any angle, and travel in the direction of that angle.
This sprite needs to have a bounding box, so I can detect collision to it.
This bounding box needs to:
Follow the sprite around the screen.
Rotate when the sprite rotates.
Obviously it should be invisible, but right now I'm drawing the box on the screen to see if it works. It doesn't.
My problem is this:
When the sprite travels parallel to the x axis or y axis, the box follows correctly and keeps 'wrapping' the sprite precisely.
But when the sprites travles diagonaly, the box doesn't follow the sprite correctly.
Sometimes it moves too much along the x axis and too little along the y axis. Sometimes the opposite. And maybe sometimes too much both or too little on both. Not sure.
Could you look at my code and tell me if you see anything wrong?
(Please note: The bounding box most of the time is actually just two arrays of coordinates, each one containing 4 values. The coordinates are used to form a Polygon when collision is checked, or when the box is drawn to the screen).
Relevant code from the Entity class, the superclass of Tank:
int[] xcoo = new int[4]; // coordinates of 4 vertices of the bounding box.
int[] ycoo = new int[4];
double x,y; // current position of the sprite.
double dx,dy; // how much to move the sprite, and the vertices of the bounding box.
double angle; // current angle of movement and rotation of sprite and bounding-box.
// Returns a Polygon object, that's the bounding box.
public Polygon getPolyBounds(){ return new Polygon(xcoo,ycoo,xcoo.length) ; }
public void move(){
// Move sprite
x += dx;
y += dy;
// Move vertices of bounding box.
for(int i=0;i<4;i++){
xcoo[i] += dx;
ycoo[i] += dy;
}
// Code to rotate the bounding box according to the angle, will be added later.
// ....
}
Relevant code from the Board class, the class that runs most of the game.
This is from the game-loop.
// keysPressed1 is an array of flags to tell which key is currently pressed.
// if left arrow is pressed
if(keysPressed1[0]==true)
tank1.setAngle(tank1.getAngle()-3);
// if right arrow is pressed
if(keysPressed1[1]==true)
tank1.setAngle(tank1.getAngle()+3);
// if up arrow is pressed (sets the direction to move, based on angle).
if(keysPressed1[2]==true){
tank1.setDX(2 * Math.cos(Math.toRadians(tank1.getAngle())));
tank1.setDY(2 * Math.sin(Math.toRadians(tank1.getAngle())));
tank1.move(); // should move both the sprite, and it's bounding box.
}
Thanks a lot for your help. If you need me to explain something about the code so you can help me, please say so.
Your sprite is using doubles and your bounding box is using ints, see these declarations:
int[] xcoo = new int[4];
double x, y
And the following updates:
(double dx, dy, showing it is a double)
x += dx
xcoo[i] += dx
In the latter (the bounding box) you are adding an int to a double which causes it to drop it's decimal places as it is being cast to an integer.
Hence why they do not follow the sprite exactly, as an int can never follow a double.
To solve this you need xcoo, ycoo and corresponding methods to work with double instead of int.
Update: So Polygon only takes Integers appereantly, to solve that take a look at the following question: Polygons with Double Coordinates
You should be using Path2D.Double

JSlider-Advice needed [duplicate]

I have a problem with JSlider in Java I have drawn a circle A, and I want to put ANOTHER circle B inside the first circle A. I want to place the CENTRE of the second circle B at the same coordinate with the centre of the first circle A, and then I want to use JSlider to INCREASE or DECREASE the radius of circle B. The trouble is, when you increase or decrease the slider, the CENTRE of circle B does not stay aligned with the centre of A. Basically, I want two circles with the SAME centre. Can someone point out my mistake, please?
slider1 = new JSlider(JSlider.HORIZONTAL,10,100,10);
window.add(slider1);
slider1.addChangeListener(this);
Graphics paper = panel.getGraphics();
int slider1Value = slider1.getValue();
paper.setColor(Color.white);
paper.fillRect(0, 0, 500, 500);
paper.setColor(Color.pink);
paper.fillOval(20,20,100,100); // this is circle A
paper.drawOval(60,60,slider1Value,slider1Value); // this is circle B slider
Because you have to change position of top-left "corner" of circle. If you change radius, the circle is bigger/smaller so it's obvious if you don't change position of top-left cornet, centers of 2 circles won't be aligned
Theory
From Graphics.drawOval() javadoc. When you draw a circle their (x,y) coordinates are not its center but its top-left corner. In order to make your B circle aligned with A circle, you need to calculate its (x,y) coordinates relatives to the last one:
Circle A: (x,y) = (20,20); diameter = 100 ==> radius = 50; center = (x + radius, y + radius) = (70,70)
Well, you have your center now: (70,70). Now, slider1Value is your new radius so you need to calculate circle B (x,y) coordinates:
Circle B: center = (70,70); (x,y) = (centerX - radius, centerY - radius) = (70 - slider1Value, 70 - slider1Value)
Finally, circle B width and height are equals to its diameter:radius * 2 = slider1Value * 2.
Facts
Make this change and it will work like a charm:
paper.drawOval(70 - slider1Value, 70 - slider1Value, 2*slider1Value, 2*slider1Value);

jFreeChart: How to draw the y-axis in a line chart

With Java, is there any way of drawing the y-axis in a line chart with JFreeChart?
I need to show both the x-axis and the y-axis, and I already draw the x-axis with the flowing code:
LineFunction2D x_axis = new LineFunction2D(0, 0);
XYDataset xdataset = DatasetUtilities.sampleFunction2D(
x_axis, min_x-min_x/15, max_x+max_x/15, 100, "X-Axis");
xyplot.setDataset(1, xdataset);
I think it's impossible to draw the line x = 0 with LineFunction2D, which take the value of a and b of the equation y = ax + b.
Maybe there is a function to call to show the axis because I have seen a demo showing this.
There is a method in the XYPlot class:
public void setDomainZeroBaselineVisible(boolean visible);
...that will show a line at the zero value on the domain (x) axis.
Y=aX+b can be displayed using ChartFactory.createLineChart. This way, you get your series, the X and Y axis in one go.

Categories