I'm trying to draw 10,000 circles in JavaFX but it seems like its not working and I'm not even able to draw a single circle. Actually it trows me an error:
This is the code that I currently have:
public class RandomCircles extends Application {
private Random randomNumbers;
private int count;
private final double MAX_X = 600;
private final double MAX_Y = 300;
private final int FINAL_CIRCLES = 10000;
public void start(Stage primaryStage){
Circle initCircle = new Circle();
initCircle.setStroke(Color.BLACK);
initCircle.setStrokeWidth(3);
initCircle.setRadius(1);
for(count = 0; count <= FINAL_CIRCLES; count++){
initCircle.setCenterX(randomNumbers.nextInt((int) MAX_X));
initCircle.setCenterY(randomNumbers.nextInt((int) MAX_Y));
}
Group baseDemo = new Group(initCircle);
// Scene scene = new Scene(baseDemo, MAX_X, MAX_Y);
Scene scene = new Scene(baseDemo);
scene.setFill(Color.WHITE);
scene.getWidth();
primaryStage.setTitle("10,000");
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
launch(args);
}
}
Can somebody also tell me if using the setCenterX/Y is the right approach to create the circles in random locations?
Thanks.
UPDATE: To the person who though of my post as a duplicate, it is not. My problem comes from my logic that I implemented in my code not from a NullPointerException(not really) error. , which was wrong. Some guy already helped me to solve it.
After fixing the runtime error you are getting, your code only draws one circle. That's because you only add one circle to your scene graph. The for loop basically does nothing. The last X and Y coordinates for the circle's center are used to draw the single, solitary circle. You need to add ten thousand circles.
In the code below, I changed 10_000 to 100 (one hundred) since 10_000 has too many overlapping circles in the stage dimensions that you set. I also increased each circle's radius.
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class RandomCircles extends Application {
private Random randomNumbers = new Random();
private int count;
private final double MAX_X = 600;
private final double MAX_Y = 300;
private final int FINAL_CIRCLES = 100;
public void start(Stage primaryStage){
Group baseDemo = new Group();
for(count = 0; count <= FINAL_CIRCLES; count++){
Circle initCircle = new Circle();
initCircle.setStroke(Color.BLACK);
initCircle.setStrokeWidth(3);
initCircle.setRadius(5);
initCircle.setCenterX(randomNumbers.nextInt((int) MAX_X));
initCircle.setCenterY(randomNumbers.nextInt((int) MAX_Y));
baseDemo.getChildren().add(initCircle);
}
Scene scene = new Scene(baseDemo);
scene.setFill(Color.WHITE);
scene.getWidth();
primaryStage.setTitle("100");
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Change this line:
private Random randomNumbers;
to this:
private Random randomNumbers = new Random();
Your code is assuming the the Random object will be allocated like the other member variables, but it is an object and must be created with new.
Related
This is my first post, so please forgive any mistakes I make. Using Java, I've made a recreation of the classic arcade game, Snake. The game mechanics, title screen, and end screen all function properly and look correct; however, my game board currently looks horrendous.
I implemented the game's board by making a TilePane that holds a certain width and height of Rectangles. For some reason, I can clearly see a separation between each Rectangle where the background of the Region containing the TilePane peeps through. To understand how I formatted the board, here's the code where I place each Rectangle in the TilePane:
private final Rectangle[][] board; // board containing the Rectangles
public Board(int width, int height) {
// grid to display each Rectangle
TilePane playableGrid = new TilePane(Orientation.VERTICAL);
playableGrid.setPrefRows(height);
this.setBackground(Background.EMPTY);
// initialize the board of Rectangles
board = new Rectangle[width][height];
// places each Rectangle in the board and playableGrid
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
board[x][y] = new Rectangle(24, 24);
playableGrid.getChildren().add(board[x][y]);
} // for
} // for
this.getChildren().add(playableGrid); // add the playableGrid in the VBox
} // Board
Also, here's a link showing how the problem: https://imgur.com/a/d25iY3i. Any help would be greatly appreciated! I fiddled around with the Stroke property found in the Shape class, but I could not find any solution.
Edit: I'll provide a minimal reproducible example of the code to display the problem. Also, I forgot to mention that I'm using XMing to run the Application on a Windows laptop. Here's a quick example that you should be able to copy and paste into a class (it shows one extra Rectangle on the right, not sure why):
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Example extends Application {
public static void main(String[] args) {
Application.launch(Example.class, args);
}
public Example() {}
#Override
public void start(Stage stage) {
/** Setup TilePane where game would be played */
TilePane playableGrid = new TilePane(Orientation.VERTICAL);
playableGrid.setPrefRows(25);
for (int x = 0; x < 25; x++) {
for (int y = 0; y < 25; y++) {
// The playableGrid consists of 25 rows of Rectangles
Rectangle rectangle = new Rectangle(20, 20);
rectangle.setFill(Color.CADETBLUE);
playableGrid.getChildren().add(rectangle);
} // for
} // for
/** In my game, a Region with a specific background holds the playableGrid */
VBox root = new VBox();
root.setMinSize(500, 500);
root.setBackground(new Background(new BackgroundFill(Color.AZURE,
CornerRadii.EMPTY, Insets.EMPTY)));
root.getChildren().add(playableGrid);
/** Setup scene */
Scene scene = new Scene(root);
/** Setup stage */
stage.setScene(scene);
stage.setOnCloseRequest(event -> Platform.exit());
stage.sizeToScene();
stage.show();
}
}
Clearly, you can see the background of the VBox holding the TilePane.
i'm looking for a system to render this animation of the line dynamic with variable change:
public class FXMLDocumentController implements Initializable {
private long batt = 0;
#FXML
private Line lancettaBatteria;
public long mappa(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Random random = new Random();
batt = random.nextInt(100);
long valMappatoBatteria = this.mappa(batt, 0, 100, -40, 135);
Rotate rotazioneBatteria = new Rotate();
lancettaBatteria.getTransforms().add(rotazioneBatteria);
Timeline timelineBatteria = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotazioneBatteria.angleProperty(), -40)),
new KeyFrame(Duration.seconds(3), new KeyValue(rotazioneBatteria.angleProperty(), valMappatoBatteria)));
timelineBatteria.play();
}
with this code it show only the first random number, my target is to move the line for infinite time with the relative random number generated(i need the random number for display the line in particular position), is this possible? i try to sorround all with a while(true)
public void initialize(URL url, ResourceBundle rb) {
while(true){
Random random = new Random();
batt = random.nextInt(100);
long valMappatoBatteria = this.mappa(batt, 0, 100, -40, 135);
Rotate rotazioneBatteria = new Rotate();
lancettaBatteria.getTransforms().add(rotazioneBatteria);
Timeline timelineBatteria = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotazioneBatteria.angleProperty(), -40)),
new KeyFrame(Duration.seconds(3), new KeyValue(rotazioneBatteria.angleProperty(), valMappatoBatteria)));
timelineBatteria.play();
}
}
but the app stop to work.
General Approaches
In general, an infinite animation in Java can be achieved multiple ways.
Here are a few:
Set the cycle count to indefinite to have an animation play forever:
Timeline.setCycleCount(Timeline.INDEFINITE);
Also setAutoReverse to true if you want it to go back and forth.
Use an AnimationTimer.
Use a Timeline and add an onFinished handler to the timeline which updates some relevant Keyframes within the timeline as necessary and then plays the timeline from start again.
The third approach of using an onFinishedHandler is the approach followed in the specific example below.
Specific Example
This example is based upon the requirements of your question as I understand them. I have no idea what why you are trying to do this. But as far as I understand what you are trying to do, the following app will do it.
Start position of each rotation:
Random max position of a rotation:
What it does is create a timeline which will update a value in a rotation transform for a line continuously. The line starts from a starting rotation angle, animates to a random maximum value and then animates back. Once the line reaches its starting position, a new random maximum value for the rotation is generated and the line animates to this new maximum value and back again. The process continues indefinitely. The setOnFinishedHandler of the timeline animation is the point which calculates the new random maximum value and updates the keyframe for the maximum animation value appropriately.
So that may or may not be exactly what you are trying to do, but perhaps it is enough for you to implement what you need.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class VariableLine extends Application {
private static final double S = 100;
#Override
public void start(Stage stage) throws Exception {
RandomRotator randomRotator = new RandomRotator();
Line line = new Line(0, S, S, S);
randomRotator.getRotate().setPivotY(S);
line.getTransforms().add(randomRotator.getRotate());
Label maxValueText = new Label(randomRotator.getMaxAngle() + "");
maxValueText.textProperty().bind(randomRotator.maxAngleProperty().asString());
stage.setScene(new Scene(new Pane(maxValueText, line), S, S * 2));
stage.show();
randomRotator.getTimeline().play();
}
public static void main(String[] args) {
launch(args);
}
}
class RandomRotator {
private static final Random random = new Random(42);
private static final double INIT_ANGLE = -40;
private static final double MAX_ANGLE = 90;
private static final Duration ROTATION_DURATION = Duration.seconds(3);
private final ReadOnlyDoubleWrapper maxAngle = new ReadOnlyDoubleWrapper(INIT_ANGLE);
private final Timeline timeline = new Timeline();
private final Rotate rotate = new Rotate(INIT_ANGLE);
RandomRotator() {
timeline.getKeyFrames().addAll(
new KeyFrame(
Duration.seconds(0),
new KeyValue(rotate.angleProperty(), INIT_ANGLE)
),
new KeyFrame(
ROTATION_DURATION.divide(2),
new KeyValue(rotate.angleProperty(), maxAngle.get())
),
new KeyFrame(
ROTATION_DURATION,
new KeyValue(rotate.angleProperty(), INIT_ANGLE)
)
);
timeline.setOnFinished(event -> {
maxAngle.set(random.nextInt((int) MAX_ANGLE));
timeline.getKeyFrames().set(
1,
new KeyFrame(
ROTATION_DURATION.divide(2),
new KeyValue(rotate.angleProperty(), maxAngle.get())
)
);
timeline.playFromStart();
});
}
Rotate getRotate() {
return rotate;
}
public double getMaxAngle() {
return maxAngle.get();
}
public ReadOnlyDoubleProperty maxAngleProperty() {
return maxAngle.getReadOnlyProperty();
}
public Timeline getTimeline() {
return timeline;
}
}
I am going to school for programming in java. I received a program where i have to use a scrollbar to change the width of an imageView. My question is it even possible with the scrollbar API in JavaFX?
Alright Here is my code.
sb = new ScrollBar();
sb.setMax(100);
sb.setMin(0);
lastValue = 500;
sb.setValue(lastValue);
sb.setUnitIncrement(1);
sb.blockIncrementProperty();
sb.setOnScroll(e -> FacePart.getPart().scrollAction(lastValue));
this is where i am having the issue. communicating back and forth between the class that this code is in and my method that is in another class.
here is the method in the other class.
Other method
#Override
public void scrollAction(double j) {
/*Global variable*/ lastScrollValue = j;
iv.setFitWidth(300 + 2 * lastScrollValue);
}
This can be done, but it shouldn't be done using the onScroll event.
Add a ChangeListener to the value property instead:
sb.valueProperty().addListener((observable, oldValue, newValue) -> {
FacePart.getPart().scrollAction(newValue.doubleValue());
});
Or simply use bindings:
iv.fitWidthProperty().bind(sb.valueProperty().multiply(2).add(300));
At least I guess that's what you want to do. I'm not sure why you'd use a "global" there.
Furthermore:
sb.blockIncrementProperty();
is effectively a NOP here. If you need to set (or get) the value, you should do it using the provided setter (or the getter):
sb.setBlockIncrement(someValue);
Doing this via the property is less readable and does the same.
Also usually you'd use a Slider for this (since the handle size doesn't seem to have a meaning in this case).
I agree with Fabian, just use a Slider for this task, rather than a ScrollBar. A Slider is a more suitable control.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class SmurfObesityMeter extends Application {
private static final double DEFAULT_SIZE = 128;
private static final double MIN_WIDTH = DEFAULT_SIZE / 2;
private static final double MAX_WIDTH = DEFAULT_SIZE * 2;
#Override
public void start(final Stage stage) throws Exception {
final Image image = new Image(IMAGE_LOC);
final ImageView imageView = new ImageView(image);
StackPane imagePane = new StackPane(imageView);
imagePane.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
imagePane.setPrefSize(MAX_WIDTH, DEFAULT_SIZE);
imagePane.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
final Slider slider = new Slider(MIN_WIDTH, MAX_WIDTH, DEFAULT_SIZE);
imageView.fitWidthProperty().bind(slider.valueProperty());
final VBox layout = new VBox(10, imagePane, slider);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static final String IMAGE_LOC =
"http://icons.iconarchive.com/icons/designbolts/smurfs-movie/128/smurfette-icon.png";
}
I currently have a 3 class java application, I am trying to create a simple game using JavaFX. In My GameCore class I am trying to create an instance of gameGrid. but when I use "grid = new gameGrid(int, int, int, int);" eclipse tells me gameGrid is undefined and suggests I create the method, when I do as eclipse asks, it places a private method gameGrid in my gameCore class, but gameGrid is supposed to be the constructor for gameGrid.class. I have already restarted the project and cleaned the project to no avail.
public class gameCore {
gameGrid grid;
public gameCore(){
getGrid();
}
public void getGrid(){
grid = gameGrid(32, 32, 10, 10); //Error is here, underlining "gameGrid"
//Also using gameGrid.gameGrid(32,32,10,10); does not work either, still says its undefined
/*
This is the code that Eclipse wants to place when I let it fix the error, and it places this code in this class.
private gameGrid gameGrid(int i, int j, int k, int l) {
// TODO Auto-generated method stub
return null;
}
*/
}
}
public class gameGrid {
protected int[][] grid;
protected int tileWidth;
protected int tileHeight;
public gameGrid(int tileWidth, int tileHeight, int horizTileCount, int vertTileCount){
//Create Grid Object
grid = new int[vertTileCount][];
for(int y = 0; y < vertTileCount; y++){
for(int x = 0; x < horizTileCount; x++){
grid[y] = new int[horizTileCount];
}
}
this.tileWidth = tileWidth;
this.tileHeight = tileHeight;
}
}
import java.awt.Dimension;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class gameGUI extends Application {
Dimension screenDimensions = new Dimension(java.awt.Toolkit.getDefaultToolkit().getScreenSize());
public static void main(String[] args){
launch(args);
}
public void start(Stage stage) throws Exception {
Canvas c = new Canvas();
StackPane sp = new StackPane();
Scene scene = new Scene(sp, screenDimensions.width, screenDimensions.height);
sp.getChildren().add(c);
stage.setScene(scene);
gameCore game = new gameCore();
stage.show();
}
}
What you are missing is "new" for instantiation, i. e. you need to write
grid = new gameGrid(32, 32, 10, 10);
In Java classes start with uppercase character, you should read the guidelines.
In case you want to see a grid being done in JavaFX with java nodes instead of a canvas, you can look at the code of the question I asked recently.
I want to display 5 randomly positioned and colored circles. It was easy part. Now I want to adjust this code to be an animation. This application should generate random circles endlessly but the condition is that it should keep only last five circles on the screen. This is where I got stuck. JavaFx provides ListChangeListener. I think it is what I should use. But how?
The following is my unfinished code:
import java.util.Random;
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class RandomColorTest extends Application {
int radius = 20;
int sceneWidth = 300;
int sceneHeight = 300;
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, sceneWidth,sceneHeight));
for (int i = root.getChildren().size(); i < 5; i++) {
root.getChildren().add(createCircle());
// the following should convey the idea:
// if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
// add one new element
// if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
// add one new element
// and so on
root.getChildren().addListener(new ListChangeListener<E>() {
#Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends E> arg0) {
// TODO Auto-generated method stub
}
});
}
}
// Create randomly positioned and colored circle
private Circle createCircle() {
final Circle circle = new Circle();
circle.setRadius(radius);
Random r = new Random();
int rCol1 = r.nextInt(256);
int rCol2 = r.nextInt(256);
int rCol3 = r.nextInt(256);
int rX = radius+r.nextInt(sceneWidth);
if (rX>sceneWidth-radius) {
rX=rX-2*radius;
}
int rY = radius+r.nextInt(sceneHeight);
if (rY>sceneHeight-radius) {
rY=rY-2*radius;
}
circle.setLayoutX(rX);
circle.setLayoutY(rY);
circle.setStroke(Color.BLACK);
circle.setFill(Color.rgb(rCol1,rCol2,rCol3));
System.out.println(rCol1+"-"+rCol2+"-"+rCol3+"-"+rX+"-"+rY);
return circle;
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
After having managed to make ListChangeListener compile without errors it doesn't still work the way expected. Changes made to for loop:
for (int i = root.getChildren().size();;i++) {
final ObservableList<Node> ol = root.getChildren();
// the following should convey the idea:
// if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
// add one new element
// if the collection holds 5 elements then keep least recently generated element for 1 second and then delete it
// add one new element
// and so on
ol.add(createCircle());
ol.addListener( new ListChangeListener<Node>(){
#Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends Node> arg0) {
// TODO Auto-generated method stub
System.out.println("one new element added, size:"+ol.size());
if (ol.size()==5) {
ol.remove(0);
}
}
});
}
For loop is defined to loop infinitely (probably not the right way to solve this problem also) and I can see from console that circles are removed and added during the program run. Alas, I can't see GUI anymore.
A similar question was also asked on the on the Oracle forums last year.
Here is sample solution using Timeline, which I prefer to a solution relying on worker threading. Though both can get the job done, I find using the JavaFX animation APIs more elegant and less error prone.
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class FiveAutoCircleExample extends Application {
private static final Random r = new Random();
public static final int SCENE_SIZE = 800;
public static void main(String[] args) throws Exception { launch(args); }
public void start(final Stage stage) throws Exception {
final Group circles = new Group();
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(.5),
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
while (circles.getChildren().size() >= 5) circles.getChildren().remove(0);
int radius = 10 * r.nextInt(20);
circles.getChildren().add(
new Circle(
r.nextInt(SCENE_SIZE - radius * 2) + radius, r.nextInt(SCENE_SIZE - radius * 2) + radius,
radius,
new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble())
)
);
}
})
);
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
// display the scene.
stage.setScene(new Scene(circles, SCENE_SIZE, SCENE_SIZE, Color.CORNSILK));
stage.show();
}
}
In your code, you have some mistakes:
the GUI is not shown because, the execution flow never reaches the primaryStage.show(); due to infinite loop in the init(primaryStage);.
new ListChangeListener is added again and again in a loop. However you should add it only once in normal situations.
You are manipulating the ol (ol.remove(0);) in its own listener which triggers the new change event recursively.
As a solution: periodic tasks, long-time background executions can be separated to a different thread.
#Override
public void start(Stage primaryStage) throws Exception {
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, sceneWidth, sceneHeight));
final ObservableList<Node> ol = root.getChildren();
new Thread(new Runnable() {
#Override public void run() {
while (true) {
try {
// Wait for 2 seconds.
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("ol size:" + ol.size());
if (ol.size() == 5) {
ol.remove(0);
}
ol.add(createCircle());
}
});
}
}
}).start();
primaryStage.show();
}
I have only changed the content of start(Stage primaryStage). There is no need to add a listener. This solution is very quick but not elegant way. You must manage the thread yourself. For more elegant and modern approach refer to Worker Threading in JavaFX 2.0.
In addition, if you really want a real animation then see the example Colorful Circles Application.