I have created a program for java fx to enter points in for an array. The points are to be shown with a line drawn between the maximal points. Also if you left mouse click it adds a point and recalculates the maximal points. If you right click it removes the point from the display. The problem I am running into is I am reading in a file with the points but nothing is showing in my display. I have even tried to manually add points and still nothing shows in the display. What am I missing?
I have tried everything I can to understand why no points are showing and am at a loss. I know it is something simple, but I cannot figure it out.
The points that should be added from the text file are
This is what it should look like when initially ran
This is what it actually looks like
I have corrected the mouse event so if I click in the upper left it will add points
Point.java
package application;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public final class Point implements Comparable<Point> {
private final double x;
private final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean isBelowAndLeftOf(Point other) {
return this.x <= other.getX() && this.y <= other.getY();
}
@Override
public int compareTo(Point other) {
return Double.compare(this.x, other.getX());
}
}
MaximalPointsPane.java
package application;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.input.MouseButton;
import java.util.Collections;
import java.util.ArrayList;
public class MaximalPointsPane extends Pane {
private final ArrayList<Point> points;
private final ArrayList<Point> maximalPoints;
public MaximalPointsPane(ArrayList<Point> points) {
this.points = points;
this.maximalPoints = new ArrayList<>();
findMaximalPoints();
drawMaximalLines();
this.setOnMouseClicked(this::handleMouseClick);
}
private void findMaximalPoints() {
maximalPoints.clear();
for (Point p1 : points) {
boolean isMaximal = true;
for (Point p2 : points) {
if (p1 != p2 && p2.isBelowAndLeftOf(p1)) {
isMaximal = false;
break;
}
}
if (isMaximal) {
maximalPoints.add(p1);
}
}
}
private void drawMaximalLines() {
getChildren().clear();
for (int i = 0; i < maximalPoints.size() - 1; i++) {
Point p1 = maximalPoints.get(i);
Point p2 = maximalPoints.get(i + 1);
Line line = new Line(p1.getX(), p1.getY(), p2.getX(), p2.getY());
getChildren().add(line);
}
}
private void handleMouseClick(MouseEvent event) {
if (event.isPrimaryButtonDown()) {
// Left click
double x = event.getX();
double y = event.getY();
points.add(new Point(x, y));
findMaximalPoints();
drawMaximalLines();
} else if (event.isSecondaryButtonDown()) {
// Right click
double x = event.getX();
double y = event.getY();
Point pointToRemove = null;
for (Point p : points) {
if (Math.abs(p.getX() - x) < 5 && Math.abs(p.getY() - y) < 5) {
pointToRemove = p;
break;
}
}
if (pointToRemove != null) {
points.remove(pointToRemove);
findMaximalPoints();
drawMaximalLines();
}
}
}
}
Main.java
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
ArrayList<Point> points = new ArrayList<>();
try {
File file = new File("points.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNext()) {
double x = scanner.nextDouble();
double y = scanner.nextDouble();
points.add(new Point(x, y));
}
scanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
MaximalPointsPane pane = new MaximalPointsPane(points);
Scene scene = new Scene(pane, 500, 500);
stage.setTitle("Maximal Points");
stage.setScene(scene);
stage.show();
}
}
There are three issues here:
isBelowAndLeftOf(...)
in the Point
class is actually testing if the point is above and left of the provided point. (Remember the y-coordinate increases as you go down the screen, not up.)false
.To fix the first problem, add some small circles representing each point. (I have also changed the method name to better represent what it is doing.)
private void drawMaximalLinesAndPoints() {
getChildren().clear();
for (int i = 0; i < maximalPoints.size() - 1; i++) {
Point p1 = maximalPoints.get(i);
Point p2 = maximalPoints.get(i + 1);
Line line = new Line(p1.getX(), p1.getY(), p2.getX(), p2.getY());
getChildren().add(line);
}
for (Point p : points) {
getChildren().add(new Circle(p.getX(), p.getY(), 2));
}
}
After this change, on starting the application, I see the following:
This is the expected behavior. Your code for finding maximal points is finding the points for which no other point is above and to the left of it. The only points in the image with this property are the ones joined by the line (every other point has at least one point which is above and to the left of it).
To fix the second problem, change the isBelowAndLeftOf(...)
as follows:
public boolean isBelowAndLeftOf(Point other) {
return this.x <= other.getX() && this.y >= other.getY();
}
After this change, on startup I see
Again, this is expected given your algorithmic definition of "maximal point". There are three points in the image (the ones connected by lines) for which no other point is below and to the left of it. (All the other points in the image have at least one point below and left of it.) If you are expecting the image you provided, your definition of "maximal points" is different to the one you have implemented. (I do not know what that definition is; maybe you intend a maximal point to be one with nothing above and to the right of it?)
For the mouse click issue, note the following:
The method isPrimaryButtonDown()
(and also isSecondaryButtonDown()
) is the wrong method to use here. See the documentation.
Basically, a mouse click occurs when a mouse button is pressed and released. Since the mouse click event occurs following the release, no mouse button will be down at that point.
Instead, use if (event.getButton() == MouseButton.PRIMARY)
, etc:
private void handleMouseClick(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
// Left click
double x = event.getX();
double y = event.getY();
points.add(new Point(x, y));
findMaximalPoints();
drawMaximalLines();
} else if (event.getButton() == MouseButton.SECONDARY) {
// Right click
double x = event.getX();
double y = event.getY();
Point pointToRemove = null;
for (Point p : points) {
if (Math.abs(p.getX() - x) < 5 && Math.abs(p.getY() - y) < 5) {
pointToRemove = p;
break;
}
}
if (pointToRemove != null) {
points.remove(pointToRemove);
findMaximalPoints();
drawMaximalLines();
}
}
}
Or, equivalently, using switch statements:
private void handleMouseClick(MouseEvent event) {
switch (event.getButton()) {
case PRIMARY -> {
// Left click
double x = event.getX();
double y = event.getY();
points.add(new Point(x, y));
findMaximalPoints();
drawMaximalLines();
}
case SECONDARY -> {
// Right click
double x = event.getX();
double y = event.getY();
Point pointToRemove = null;
for (Point p : points) {
if (Math.abs(p.getX() - x) < 5 && Math.abs(p.getY() - y) < 5) {
pointToRemove = p;
break;
}
}
if (pointToRemove != null) {
points.remove(pointToRemove);
findMaximalPoints();
drawMaximalLines();
}
}
}
}
Here is a complete example with these fixes, which always has all points plotted and lines between the points for which no other point is below and to the left of it.
Point.java
:
public final class Point implements Comparable<Point> {
private final double x;
private final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean isBelowAndLeftOf(Point other) {
return this.x <= other.getX() && this.y >= other.getY();
}
@Override
public int compareTo(Point other) {
return Double.compare(this.x, other.getX());
}
@Override
public String toString() {
return String.format("[%.1f, %.1f]", x, y);
}
}
MaximalPointsPane.java
:
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import java.util.ArrayList;
public class MaximalPointsPane extends Pane {
private final ArrayList<Point> points;
private final ArrayList<Point> maximalPoints;
public MaximalPointsPane(ArrayList<Point> points) {
this.points = points;
this.maximalPoints = new ArrayList<>();
findMaximalPoints();
drawMaximalLinesAndPoints();
this.setOnMouseClicked(this::handleMouseClick);
}
private void findMaximalPoints() {
maximalPoints.clear();
for (Point p1 : points) {
boolean isMaximal = true;
for (Point p2 : points) {
if (p1 != p2 && p2.isBelowAndLeftOf(p1)) {
isMaximal = false;
break;
}
}
if (isMaximal) {
maximalPoints.add(p1);
}
}
}
private void drawMaximalLinesAndPoints() {
getChildren().clear();
for (int i = 0; i < maximalPoints.size() - 1; i++) {
Point p1 = maximalPoints.get(i);
Point p2 = maximalPoints.get(i + 1);
Line line = new Line(p1.getX(), p1.getY(), p2.getX(), p2.getY());
getChildren().add(line);
}
for (Point p : points) {
getChildren().add(new Circle(p.getX(), p.getY(), 2));
}
}
private void handleMouseClick(MouseEvent event) {
switch (event.getButton()) {
case PRIMARY -> {
// Left click
double x = event.getX();
double y = event.getY();
points.add(new Point(x, y));
findMaximalPoints();
drawMaximalLinesAndPoints();
}
case SECONDARY -> {
// Right click
double x = event.getX();
double y = event.getY();
Point pointToRemove = null;
for (Point p : points) {
if (Math.abs(p.getX() - x) < 5 && Math.abs(p.getY() - y) < 5) {
pointToRemove = p;
break;
}
}
if (pointToRemove != null) {
points.remove(pointToRemove);
findMaximalPoints();
drawMaximalLinesAndPoints();
}
}
}
}
}
Main.java
(modified to have initial data inline for ease of reproducibility):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Scanner;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
ArrayList<Point> points = new ArrayList<>();
String text = """
200.0 300.0
250.0 300.0
330.0 270.0
150.0 380.0
126.0 172.0
397.0 379.0
334.0 441.0
53.0 288.0
89.0 433.0
182.0 215.0
251.0 414.0""";
Scanner scanner = new Scanner(text);
while (scanner.hasNext()) {
double x = scanner.nextDouble();
double y = scanner.nextDouble();
points.add(new Point(x, y));
}
scanner.close();
MaximalPointsPane pane = new MaximalPointsPane(points);
Scene scene = new Scene(pane, 500, 500);
stage.setTitle("Maximal Points");
stage.setScene(scene);
stage.show();
}
}
A couple of other things to note, that are different between your implementation and the image you posted of your desired behavior:
Comparable
implementation, you can simply sort them after calculating them)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With