BlackJack javafx hit button doesn't work - button

I'm having trouble getting the Hit button to work in my BlackJack program. I tried programming the action for the hit button in the setOnAction block as well as lambda, but I get an error about the variables not being final. So, I tried it this way, but I don't think my variables are carrying over. It's probably something very simple. Please help if you can. Thanks!
import java.util.Arrays;
import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class BlackJackGame extends Application {
public String btHitY;
public int NumberPlayerCards;
public int NumberDealerCards;
public int NUMBER_OF_CARDS;
public int PlayerCards[];
public int DealerCards[];
public Image imagesP[];
public Image imagesD[];
public int deck[];
public String URLBase;
public GridPane pane;
public BlackJackGame() {
this.btHitY = " ";
this.btHitY = new String();
}
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
int[] deck = new int[52];
String[] suits = {"Spades", "Hearts", "Diamonds", "Clubs"};
String[] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"};
int NUMBER_OF_CARDS = 4;
String URLBase = "http://www.cs.armstrong.edu/liang/common/image/card/";
//Initialize the cards
for (int i = 0; i < deck.length; i++)
deck[i] = i;
//Shuffle the cards
for (int i = 0; i < deck.length; i++) {
//Generate an index randomly
int index = (int)(Math.random() * deck.length);
int temp = deck[i];
deck[i] = deck[index];
deck[index] = temp;
}
int NumberPlayerCards = 2;
int NumberDealerCards = 2;
int[] PlayerCards = new int[50];
int[] DealerCards = new int[50];
//Display the first cards
for (int i = 0; i < 8; i++) {
String suit = suits[deck[i] / 13];
String rank = ranks[deck[i] % 13];
System.out.println("Card number " + deck[i] + ": " + rank + " of " + suit);
}
for (int i = 0; i < NumberPlayerCards; i++)
PlayerCards[i] = deck[i * 2];
for (int i = 0; i < NumberDealerCards; i++)
DealerCards[i] = deck[i * 2 + 1];
// Create a pane to hold the image views
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setPadding(new Insets(5, 5, 5, 5));
pane.setHgap(5);
pane.setVgap(5);
Image[] imagesP = new Image[50];
Image[] imagesD = new Image[50];
for (int i = 0; i < NumberPlayerCards; i++) {
int cardForPrint = PlayerCards[i] + 1;
System.out.println(URLBase + cardForPrint + ".png");
imagesP[i] = new Image(URLBase + cardForPrint + ".png");
}
for (int i = 0; i < NumberDealerCards; i++) {
int cardForPrint = DealerCards[i] + 1;
System.out.println(URLBase + cardForPrint + ".png");
imagesD[i] = new Image(URLBase + cardForPrint + ".png");
}
//rotate flag image to cover dealer card
Image flag = new Image("http://www.cs.armstrong.edu/liang/common/image/us.gif");
ImageView imageFlag = new ImageView(flag);
imageFlag.setRotate(90);
imageFlag.setFitHeight(75);
imageFlag.setFitWidth(95);
pane.add(new Label("Player Cards"), 0, 0);
pane.add(new ImageView(imagesP[0]), 1, 0);
pane.add((imageFlag), 1, 1);
pane.add(new Label("Dealer Cards"), 0, 1);
pane.add(new ImageView(imagesP[1]), 2, 0);
pane.add(new ImageView(imagesD[1]), 2, 1);
Button btHit = new Button("Hit");
Button btStay = new Button("Stay");
pane.add(btHit, 1, 2);
pane.add(btStay, 2, 2);
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 1200, 700);
primaryStage.setTitle("Black Jack"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
HitHandlerClass handlerHit = new HitHandlerClass();
btHitY = " ";
btHit.setOnAction(handlerHit);
/* if (btHitY.equals("Hit")); {
NumberPlayerCards = NumberPlayerCards + 1;
NUMBER_OF_CARDS = NUMBER_OF_CARDS + 1;
PlayerCards[NumberPlayerCards - 1] = deck[NUMBER_OF_CARDS - 1];
for (int j = 0; j < NumberPlayerCards; j++){
System.out.println(PlayerCards[j]);
}
System.out.println(NumberPlayerCards);
int CardForPrint2 = PlayerCards[NumberPlayerCards - 1] + 1;
imagesP[NumberPlayerCards - 1] = new Image(URLBase + CardForPrint2 + ".png");
pane.add(new ImageView(imagesP[NumberPlayerCards - 1]), NumberPlayerCards, 0);
btHitY = " ";
primaryStage.show();
} */
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
* #param args
*/
public static void main(String[] args) {
launch(args);
}
class HitHandlerClass implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
NumberPlayerCards = NumberPlayerCards + 1;
NUMBER_OF_CARDS = NUMBER_OF_CARDS + 1;
PlayerCards[NumberPlayerCards - 1] = deck[NUMBER_OF_CARDS - 1];
for (int j = 0; j < NumberPlayerCards; j++){
System.out.println(PlayerCards[j]);
}
System.out.println(NumberPlayerCards);
int CardForPrint2 = PlayerCards[NumberPlayerCards - 1] + 1;
imagesP[NumberPlayerCards - 1] = new Image(URLBase + CardForPrint2 + ".png");
pane.add(new ImageView(imagesP[NumberPlayerCards - 1]), NumberPlayerCards, 0);
btHitY = " ";
}
}
}

You declare this global
public int deck[];
and then local
int[] deck = new int[52];
So the global never gets initialized and gives you a Nullpointer Exception.
Solution:
deck = new int[52];
Same for the other variables.

public class BlackJackGame extends Application {
//snip
public int NUMBER_OF_CARDS;
public int deck[];
//snip
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
int[] deck = new int[52];
int NUMBER_OF_CARDS = 4;
//snip
You've declared deck[] and NUMBER_OF_CARDS at a higher scope, then redeclared them later, at a lower scope.
To fix these issues, simply remove the type declaration in the start method.
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//Create array deck, suit string, and rank string
deck = new int[52];
NUMBER_OF_CARDS = 4;
//snip
As a side note, I would like to recommend you read up a little bit on Code conventions for Java/Javafx, as they help other programmers read your code by providing a standard style.

Related

Trying to deduce location of mouse drag with JavaFX Gridpane

Note: 25 years with Java, 2.5 hours (almost) with JavaFX.
I want to be able to highlight all cells of a GridPane that the mouse is dragged across - i.e. everything that intersects with the rectangle cornered by the click point and the current drag point. I can do that if all children are 1x1, but with mixed sizes I'm having no joy.
For example, if the top row has 1 1 column cell (A) and 1 2 column cell (B) and the bottom has 1 2 column cell (C) and 1 1 column cell (D), if I click in A and drag down into C I can highlight both. However, I cannot figure out when I drag into the right half of C so that B should be highlighted.
Sample Board:
Apologies for the HSCE - it's a bit long but I felt stripping it down would reduce readability.
import java.util.*;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.stage.Stage;
import javafx.geometry.*;
public class AddTestSSCE extends Application
{
ArrayList<StackPane> sPanes = new ArrayList<StackPane>(); // selected panes
GridPane theGrid;
Node startNode = null;
int col0, col1;
int row0, row1;
#Override
public void start(Stage stage) {
theGrid = new GridPane();
ColumnConstraints col = new ColumnConstraints(300);
theGrid.getColumnConstraints().addAll(col, col, col);
RowConstraints row = new RowConstraints(200);
theGrid.getRowConstraints().addAll(row, row, row);
addGridPane();
theGrid.getStyleClass().add("bg-grid");
Scene scene = new Scene(theGrid, 1024, 768);
scene.getStylesheets().add("addtestssce.css");
stage.setScene(scene);
stage.show();
}
public void addGridPane() {
theGrid.setHgap(10);
theGrid.setVgap(10);
theGrid.setPadding(new Insets(0, 10, 0, 10));
StackPane theSP = sPAdd(new Label("A"));
theGrid.add(theSP, 0, 0, 1, 1);
theSP = sPAdd(new Label("B"));
theGrid.add(theSP, 1, 0, 2, 1);
theSP = sPAdd(new Label("C"));
theGrid.add(theSP, 0, 1, 2, 1);
theSP = sPAdd(new Label("D"));
theGrid.add(theSP, 2, 1, 1, 1);
theGrid.addEventFilter(MouseEvent.MOUSE_PRESSED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("We're Moving!!");
startNode = (Node)e.getTarget();
sPanes.add((StackPane)startNode);
col0 = GridPane.getColumnIndex(startNode).intValue();
row0 = GridPane.getRowIndex(startNode).intValue();
System.out.printf("Starting at %d %d\n", col0, row0);
}
});
theGrid.addEventFilter(MouseEvent.MOUSE_DRAGGED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
Node lastNode = null;
#Override
public void handle(MouseEvent e) {
Node target = (Node)e.getTarget();
double xLoc = e.getX();
double yLoc = e.getY();
Bounds bs = target.localToScene(target.getBoundsInLocal());
Node moveTarget;
if( bs.contains(xLoc, yLoc) )
{
moveTarget = target;
}
else
{
moveTarget = getContainingNode((int)xLoc, (int)yLoc);
}
if( moveTarget != null && lastNode != moveTarget )
{
col1 = GridPane.getColumnIndex(moveTarget).intValue();
row1 = GridPane.getRowIndex(moveTarget).intValue();
doHighlighting();
lastNode = moveTarget;
}
}
});
}
void doHighlighting()
{
int c0, c1, r0, r1;
c0 = col0 > col1 ? col1 : col0;
c1 = !(col0 > col1) ? col1 : col0;
r0 = row0 > row1 ? row1 : row0;
r1 = !(row0 > row1) ? row1 : row0;
Rectangle2D rec1 = new Rectangle2D(c0, r0, c1-c0+1, r1-r0+1);
System.out.printf("Box: %d %d %d %d\n", c0, c1, r0, r1);
List<Node> nodes = theGrid.getChildren();
for( Node node : nodes )
{
StackPane sp = (StackPane)node;
unhighlight(sp);
int col = GridPane.getColumnIndex(sp).intValue();
int row = GridPane.getRowIndex(sp).intValue();
if( occupiesCell(sp, rec1) )
{
highlight(sp);
}
}
}
boolean occupiesCell(Node node, Rectangle2D r1)
{
boolean result = false;
int col = GridPane.getColumnIndex(node).intValue();
int row = GridPane.getRowIndex(node).intValue();
int wid = GridPane.getColumnSpan(node).intValue();
int hei = GridPane.getRowSpan(node).intValue();
Rectangle2D r2 = new Rectangle2D( col, row, wid, hei);
return r2.intersects(r1);
}
void unhighlight(Node node)
{
if( !(node instanceof StackPane) )
{
return;
}
StackPane label = (StackPane)node;
List<String> cList = label.getStyleClass();
cList.remove("b2");
cList.add("b1");
}
void highlight(Node node)
{
if( !(node instanceof StackPane) )
{
return;
}
StackPane label = (StackPane)node;
List<String> cList = label.getStyleClass();
cList.remove("b1");
cList.add("b2");
}
private Node getContainingNode(int xLoc, int yLoc)
{
Node tgt = null;
for( Node node : theGrid.getChildren() )
{
Bounds boundsInScene = node.localToScene(node.getBoundsInLocal());
if( boundsInScene.contains(xLoc, yLoc) )
{
return node;
}
}
return tgt;
}
private StackPane sPAdd(Label label)
{
StackPane gPPane = new StackPane();
gPPane.getChildren().add(label);
gPPane.getStyleClass().addAll("b1", "grid-element");
GridPane.setFillHeight(gPPane, true);
GridPane.setFillWidth(gPPane, true);
return gPPane;
}
public static void main(String[] args)
{
launch();
}
}
.bg-grid {
-fx-background-color: slategrey;
}
.grid-element {
-fx-border-width: 10;
-fx-border-color: rgb(225, 128, 217);
-fx-background-color: rgb(247, 146, 146);
-fx-font: 36 arial;
}
.b1 {
-fx-text-base-color: white;
-fx-border-color: rgb(225, 128, 217);
}
.b2 {
-fx-text-base-color: lightgray;
-fx-border-color: rgb(233, 228, 86);
}
After a bit of back and forth with #james_d and #slaw, finally came up with a working solution.
import java.util.*;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.stage.Stage;
import javafx.geometry.*;
import javafx.css.PseudoClass;
public class AddTestSSCE extends Application
{
private static final PseudoClass highlight = PseudoClass.getPseudoClass("highlight");
ArrayList<StackPane> sPanes = new ArrayList<StackPane>(); // selected panes
GridPane theGrid;
Node startNode = null;
int x0, y0, x1, y1;
#Override
public void start(Stage stage) {
theGrid = new GridPane();
ColumnConstraints col = new ColumnConstraints(300);
theGrid.getColumnConstraints().addAll(col, col, col);
RowConstraints row = new RowConstraints(200);
theGrid.getRowConstraints().addAll(row, row, row);
addGridPane();
theGrid.getStyleClass().add("bg-grid");
Scene scene = new Scene(theGrid, 1024, 768);
scene.getStylesheets().add("addtestssce.css");
stage.setScene(scene);
stage.show();
}
public void addGridPane() {
theGrid.setHgap(10);
theGrid.setVgap(10);
theGrid.setPadding(new Insets(0, 10, 0, 10));
StackPane theSP = sPAdd(new Label("A"));
theGrid.add(theSP, 0, 0, 1, 1);
theSP = sPAdd(new Label("B"));
theGrid.add(theSP, 1, 0, 2, 1);
theSP = sPAdd(new Label("C"));
theGrid.add(theSP, 0, 1, 2, 1);
theSP = sPAdd(new Label("D"));
theGrid.add(theSP, 2, 1, 1, 1);
theGrid.addEventFilter(MouseEvent.MOUSE_PRESSED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("We're Moving!!");
startNode = (Node)e.getTarget();
sPanes.add((StackPane)startNode);
x0 = x1 = (int)e.getX();
y0 = y1 = (int)e.getY();
doHighlighting();
System.out.printf("Starting at %d %d\n", x0, y0);
}
});
theGrid.addEventFilter(MouseEvent.MOUSE_DRAGGED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
Node target = (Node)e.getTarget();
x1 = (int)e.getX();
y1 = (int)e.getY();
Bounds bs = target.localToScene(target.getBoundsInLocal());
Node moveTarget;
if( bs.contains(x1, y1) )
{
moveTarget = target;
}
else
{
moveTarget = getContainingNode( x1, y1);
}
if( moveTarget != null )
{
doHighlighting();
}
}
});
}
void doHighlighting()
{
int c0, c1, r0, r1;
c0 = x0 > x1 ? x1 : x0;
c1 = !(x0 > x1) ? x1 : x0;
r0 = y0 > y1 ? y1 : y0;
r1 = !(y0 > y1) ? y1 : y0;
Bounds dragged = new BoundingBox(c0, r0, c1-c0+1, r1-r0+1);
for (Node child : theGrid.getChildren())
{
child.pseudoClassStateChanged(highlight, dragged.intersects(child.getBoundsInParent()));
}
}
private Node getContainingNode(int xLoc, int yLoc)
{
Node tgt = null;
for( Node node : theGrid.getChildren() )
{
Bounds boundsInScene = node.localToScene(node.getBoundsInLocal());
if( boundsInScene.contains(xLoc, yLoc) )
{
return node;
}
}
return tgt;
}
private StackPane sPAdd(Label label)
{
StackPane gPPane = new StackPane();
gPPane.getChildren().add(label);
gPPane.getStyleClass().addAll("b1", "grid-element");
GridPane.setFillHeight(gPPane, true);
GridPane.setFillWidth(gPPane, true);
return gPPane;
}
public static void main(String[] args)
{
launch();
}
}
and the CSS:
.bg-grid {
-fx-background-color: slategrey;
}
.grid-element {
-fx-border-width: 10;
-fx-border-color: rgb(225, 128, 217);
-fx-background-color: rgb(247, 146, 146);
-fx-font: 36 arial;
}
.grid-element:highlight {
-fx-text-base-color: lightgray;
-fx-border-color: rgb(233, 228, 86);
}

Synchronization in Javafx

I'm trying to make a method that returns after the user clicks on a rectangle in the stage. However, when calling wait() and then later notify() when the rectangle is clicked, the program freezes. How would one make a showAndWait() method in javafx?
I'm trying to make a class that asks a multiple choice question then returns the result, the below code is this class and it's commented where I attempted to put a wait and notify call.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
public class Question{
int answer; //refers to the answer in the order of strings given
// a = 1, b = 2, c = 3, d = 4
String question;
String a,b,c,d;
public Question(String question, String a, String b, String c, String d, int answer){
this.question = question;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.answer = answer;
}
private final double sideLength = 500;
private volatile byte complete; //0 = no, 1 = true, -1 = false
public synchronized boolean showAndWait(){
complete = 0;
Stage stage = new Stage();
Pane pane = new Pane();
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.setResizable(false);
stage.setMaxHeight(sideLength);
stage.setMinHeight(sideLength);
stage.setMaxWidth(sideLength);
stage.setMinWidth(sideLength);
double width = sideLength/3;
double height = sideLength/5;
double firstX = sideLength/9;
double secondX = width + sideLength/9*2;
double firstY = height + sideLength*2/5/3;
double secondY = firstY * 2;
Color base = Color.AQUAMARINE;
Color wrong = Color.RED;
Color right = Color.GREEN;
Pane questionPane = makeAnswer(0,0,sideLength,sideLength/5, question, base, base);
Pane ansA = makeAnswer(firstX, firstY, width, height,a,base, wrong);
Pane ansB = makeAnswer(secondX, firstY, width, height,b,base, wrong);
Pane ansC = makeAnswer(firstX, secondY, width, height,c,base, wrong);
Pane ansD = makeAnswer(secondX, secondY, width, height,d,base, wrong);
pane.getChildren().addAll(questionPane, ansA, ansB, ansC, ansD);
stage.show();
//wait here, check to see if completed
System.out.println(complete);
return complete == 1;
}
private double textShiftX = 30;
private double textShiftY = 30;
private int characterLimit = 20;
private double textChangeY = 10;
private synchronized Pane makeAnswer(double x, double y,double width, double height, String s, Color base, Color afterClick){
Pane temp = new Pane();
temp.setLayoutX(x);
temp.setLayoutY(y);
Rectangle rD = new Rectangle(0, 0, width, height);
rD.setFill(base);
temp.getChildren().add(rD);
for(int i=0;s.length() > 0;i++){
String sTemp;
if(s.length()>characterLimit){
sTemp = s.substring(0,characterLimit);
while(sTemp.charAt(sTemp.length()-1) != ' ')
sTemp = sTemp.substring(0,sTemp.length()-1);
}
else
sTemp = s;
s = s.substring(sTemp.length());
Text t = new Text(textShiftX, textShiftY + textChangeY*i, sTemp);
temp.getChildren().add(t);
}
rD.setOnMouseClicked(
(e)->{
((Rectangle)e.getSource()).setFill(afterClick);
complete = 1;
//notify
});
return temp;
}

How to bounce circles and other shapes in javafx?

package application;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private static Canvas canvas;
private static int clickCount = 0;
private static int x1 = 0;
private static int y1 = 0;
static ArrayList<Circle> circleArr = new ArrayList<Circle>();
private Timeline timeline;
#Override
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Graphics Demo App");
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 800, 500);
canvas = new Canvas(800, 475);
canvas.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (clickCount % 2 == 0) {
x1 = x;
y1 = y;
} else {
int x2 = x;
int y2 = y;
int r = (int) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
int x_corner = x1 - r;
int y_corner = y1 - r;
Circle c = new Circle(x_corner, y_corner, r);
circleArr.add(c);
c.drawOn(canvas);
}
clickCount++;
}
});
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
root.setCenter(canvas);
Button circleButton = new Button("Circle");
circleButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// System.out.println("Circle");
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setStroke(Color.AQUAMARINE);
Random rand = new Random();
int x = rand.nextInt((int) canvas.getWidth());
int y = rand.nextInt((int) canvas.getHeight());
Circle c = new Circle(x, y, 50);
c.drawOn(canvas);
// gc.strokeOval(x, y, 200, 200);
}
});
Button squareButton = new Button("Square");
squareButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// System.out.println("square");
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setStroke(Color.AQUAMARINE);
Random rand = new Random();
int x = rand.nextInt((int) canvas.getWidth());
int y = rand.nextInt((int) canvas.getHeight());
int w = rand.nextInt((int) canvas.getWidth());
int h = rand.nextInt((int) canvas.getHeight());
Square s = new Square(x, y, w, h);
s.drawOn(canvas);
// gc.strokeOval(x, y, 200, 200);
}
});
Button stepButton = new Button("Step");
stepButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
step();
}
});
Button playButton = new Button("Play");
playButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
timeline = new Timeline(new KeyFrame(Duration.millis(100), ae -> step()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
});
Button stopButton = new Button("Stop");
stopButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
if (timeline != null) {
timeline.stop();
}
}
});
FlowPane buttonsPane = new FlowPane();
buttonsPane.setAlignment(Pos.CENTER);
buttonsPane.getChildren().add(circleButton);
buttonsPane.getChildren().add(squareButton);
Button saveButton = new Button("Save");
Button restoreButton = new Button("Restore");
restoreButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
restore();
}
});
buttonsPane.getChildren().add(saveButton);
buttonsPane.getChildren().add(restoreButton);
buttonsPane.getChildren().add(stepButton);
buttonsPane.getChildren().add(playButton);
buttonsPane.getChildren().add(stopButton);
root.setBottom(buttonsPane);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void step() {
clearScreen();
for (Circle c : circleArr) {
c.step(canvas);
}
}
private static void clearScreen() {
canvas.getGraphicsContext2D().fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
public static void main(String[] args) {
launch(args);
}
public static void restore() {
try {
// Setting up to read the text file
FileReader reader = new FileReader("shapes.txt");
BufferedReader bufferReader = new BufferedReader(reader);
// Read the first line in the file
String line = bufferReader.readLine();
while (line != null && line.trim().length() > 0) {
String[] parts = line.split(":");
String left = parts[0];
String right = parts[1];
if (left.equals("C")) {
String[] components = right.trim().split(" ");
int x = Integer.parseInt(components[0]);
int y = Integer.parseInt(components[1]);
int r = Integer.parseInt(components[2]);
Circle c = new Circle(x, y, r);
c.drawOn(canvas);
} else if (left.equals("S")) {
System.out.println("This is a square: " + right);
}
line = bufferReader.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
This is the circle code :-
package application;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Circle {
int x;
int y;
int radius;
int x_direction = 1;
int y_direction = 1;
int dx = 11, dy = 7;
public Circle(){
}
public Circle(int x, int y, int r){
this.x = x;
this.y = y;
this.radius = r;
}
public void print(){
System.out.println("Circle: " + x + " " + y + " " + radius);
}
public void drawOn(Canvas canvas){
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setStroke(Color.AQUAMARINE);
gc.strokeOval(x, y, 2*radius, 2*radius);
}
public void step(Canvas canvas){
x += 5 * x_direction;
y += 5 * y_direction;
if (x + radius * 2 > canvas.getWidth()){
x_direction = x_direction * -1;
}
if (y + radius * 2 > canvas.getHeight()){
y_direction = y_direction * -1;
}
if ((x - radius + dx < 0) || (x + radius + dx > canvas.getWidth()))
dx = -dx;
if ((y - radius + dy < 0) || (y + radius + dy > canvas.getHeight()))
dy = -dy;
// Move the circle.
x += dx;
y += dy;
drawOn(canvas);
}
Here is what I came up with. As you didn't say your problem, I made an example. I didn't use a time line, I just used another thread that continually updates.
package helloworld;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Matt on 17/08/16.
*/
public class AnimatedScene extends Application{
List<AnimatedCircle> circles = new ArrayList<>();
Canvas custom;
boolean quit = false;
#Override
public void start(Stage primaryStage) throws Exception {
Group root = new Group();
custom = new Canvas(600, 600);
GraphicsContext gc = custom.getGraphicsContext2D();
createBackground(gc);
root.getChildren().add(custom);
circles.add(new AnimatedCircle(50, 50));
circles.add(new AnimatedCircle(50, 100));
circles.add(new AnimatedCircle(50, 150));
circles.add(new AnimatedCircle(50, 200));
for(AnimatedCircle circle: circles) {
root.getChildren().add(circle.shape);
}
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, evt->{
quit=true;
});
startMainLoop();
}
private void startMainLoop(){
new Thread(()->{
Thread.currentThread().setName("bounce loop");
long time = System.currentTimeMillis();
int step = (int)(1000.0/60.0);
while(!(Thread.interrupted()||quit)){
synchronized (circles) {
for(int i = 0; i<circles.size(); i++){
for(int j = i+1; j<circles.size(); j++){
AnimatedCircle a = circles.get(i);
AnimatedCircle b = circles.get(j);
double dx = b.cx - a.cx;
double dy = b.cy - a.cy;
double distance = Math.sqrt(dx*dx + dy*dy);
if(distance<a.radius + b.radius){
//collision
b.vx = dx/distance;
b.vy = dy/distance;
a.vx = -dx/distance;
a.vy = dy/distance;
}
}
}
for (AnimatedCircle circle : circles) {
circle.update();
}
}
Platform.runLater(()->{
sync();
});
try{
long nt = System.currentTimeMillis();
long wait = step - (nt - time);
if(wait>=0) {
Thread.sleep(wait);
time = nt+wait;
} else{
time = nt;
}
} catch(InterruptedException exc){
//just exit.
}
}
}).start();
}
void sync(){
synchronized (circles){
GraphicsContext gc = custom.getGraphicsContext2D();
createBackground(gc);
for(AnimatedCircle circle: circles){
circle.sync();
gc.setFill(new Color(0, 0, 0, 0.2));
gc.fillOval(circle.cx - circle.radius-15, circle.cy - circle.radius-5, 50, 50);
}
}
}
void createBackground(GraphicsContext gc){
gc.setFill(Color.RED);
RadialGradient gradient = new RadialGradient(
0, 0, 300, 300, 300, false,
CycleMethod.NO_CYCLE,
new Stop(0.0, Color.RED), new Stop(1.0, Color.DARKRED));
gc.setFill(gradient);
gc.fillRect(100, 100, 400, 400);
gc.setFill(new Color(1, 0.2, 0.2, 1));
gc.fillPolygon(
new double[]{ 0, 100, 100, 0},
new double[]{ 0, 100, 500, 600},
4
);
gc.setFill(new Color(1, 0.25, 0.25, 1));
gc.fillPolygon(
new double[]{ 0, 100, 500, 600},
new double[]{ 0, 100, 100, 0},
4
);
gc.setFill(new Color(0.5, 0., 0., 1));
gc.fillPolygon(
new double[]{ 500, 600, 600, 500},
new double[]{ 100, 0, 600, 500},
4
);
gc.setFill(new Color(0.8, 0., 0., 1));
gc.fillPolygon(
new double[]{ 0, 100, 500, 600},
new double[]{ 600, 500, 500, 600},
4
);
}
public static void main(String[] args){
launch(args);
}
}
class AnimatedCircle{
public Ellipse shape;
RadialGradient gradient;
double vx;
double vy;
double cx, cy;
double speed = 5;
double radius = 25;
public AnimatedCircle(double x, double y){
cx = x;
cy = y;
vx = 2*(Math.random() - 0.5);
vy = Math.sqrt(1 - vx*vx);
if(Math.random()>=0.5){
vy = -vy;
}
shape = new Ellipse(cx, cy, radius, radius);
final String label = x + ", " + y;
shape.addEventHandler(MouseEvent.MOUSE_CLICKED, evt->{
System.out.println(label);
});
gradient = new RadialGradient(0, 0, 0.75, 0.6, 1, true, CycleMethod.NO_CYCLE, new Stop(0.0, Color.WHITE), new Stop(1.0, Color.BLACK));
shape.setFill(gradient);
}
void update(){
double nx = cx + vx*speed;
if(nx + shape.getRadiusX()>600 || nx - shape.getRadiusX()<0){
vx = -vx;
nx = cx;
}
double ny = cy + vy*speed;
if(ny + shape.getRadiusY()>600 || ny - shape.getRadiusY()<0 ){
vy = -vy;
ny = cy;
}
cy = ny;
cx = nx;
}
public void sync(){
shape.setCenterX(cx);
shape.setCenterY(cy);
}
}
Things to note:
In createBackground, I completely repaint the background. In your clear method you fill everything with the last paint. That doesn't necessarily clear your background.
I used two different methods to draw the balls. 1) I use ellipses that are added to the scene, then I update their position. 2) I draw the shadows using the graphics canvas.
Otherwise, if you can clean up your question a bit, and actually explain what is wrong somebody might be able to fix your code.

JavaFX spring physics

I tried to create a spring like behavior with JavaFX by creating draggable circles. When I drag one circle, the others should follow and simulate elasticity.
I created a boilerplate template which contains 3 circles, they are draggable via mouse. The animation is running, of course all appears still because currently speed is 0. All I need to know is how to calculate the angle and the speed of the attached particles.
It would be great if someone could help me.
Here's the code:
import java.util.ArrayList;
import java.util.List;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
public class PhysicsTest extends Application {
List<Particle> particles = new ArrayList<>();
List<Spring> springs = new ArrayList<>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
// create particles
Particle pRed = new Particle(Color.RED, 100, 100);
Particle pBlue = new Particle(Color.BLUE, 400, 200);
Particle pGreen = new Particle(Color.GREEN, 100, 300);
// red -> blue
Line lineRedBlue = new Line(100, 100, 500, 500);
lineRedBlue.setStroke(Color.BLACK);
lineRedBlue.setStrokeWidth(5);
// green -> blue
Line lineGreenBlue = new Line(100, 100, 500, 500);
lineGreenBlue.setStroke(Color.BLACK);
lineGreenBlue.setStrokeWidth(5);
// line binding
// line 1 -> 2
lineRedBlue.startXProperty().bind(pRed.centerXProperty());
lineRedBlue.startYProperty().bind(pRed.centerYProperty());
lineRedBlue.endXProperty().bind(pBlue.centerXProperty());
lineRedBlue.endYProperty().bind(pBlue.centerYProperty());
// line 3 -> 2
lineGreenBlue.startXProperty().bind(pGreen.centerXProperty());
lineGreenBlue.startYProperty().bind(pGreen.centerYProperty());
lineGreenBlue.endXProperty().bind(pBlue.centerXProperty());
lineGreenBlue.endYProperty().bind(pBlue.centerYProperty());
MouseGestures mg = new MouseGestures();
mg.makeDraggable(pRed);
mg.makeDraggable(pBlue);
mg.makeDraggable(pGreen);
root.getChildren().addAll(pRed, pBlue, pGreen, lineRedBlue, lineGreenBlue);
// add to list
particles.add( pRed);
particles.add( pBlue);
particles.add( pGreen);
// add springs
Spring s1 = new Spring( pRed, pBlue, 10, 0.5);
springs.add( s1);
Spring s2 = new Spring( pGreen, pBlue, 10, 0.5);
springs.add( s2);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
// animate
startAnimation();
}
private void startAnimation() {
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
// move particles
for (Particle p : particles) {
if (!p.selected) {
p.move();
}
}
// apply springs
for (Spring s : springs) {
s.update();
}
// move particles to new location
for (Particle p : particles) {
p.updateLocation();
}
}
};
timer.start();
}
/**
* The spring constraint and calculation. Updates particle
*/
public class Spring {
Particle p1;
Particle p2;
double length; // length it tries to obtain
double strength; // how quickly it tries to reach that length
public Spring( Particle p1, Particle p2, double length, double strength) {
this.p1 = p1;
this.p2 = p2;
this.length = length;
this.strength = strength;
}
public void update() {
double dx = p1.getCenterX() - p2.getCenterX();
double dy = p1.getCenterY() - p2.getCenterY();
double dist = Math.hypot(dx, dy);
double theta = Math.atan2(dy, dx);
double force = (length - dist) * strength;
// System.out.println( dist + ", " + Math.toDegrees( theta) + ", " + force);
// what's supposed to happen here?
p1.angle = ... // <===
p1.speed = ... // <===
p2.angle = ... // <===
p2.speed = ... // <===
}
}
/**
* The particle itself
*/
public class Particle extends Circle {
double x;
double y;
double angle = 0.0;
double speed = 0.0;
double mass = 1;
boolean selected = false;
public Particle(Color color, double x, double y) {
super(x, y, 50);
this.x = x;
this.y = y;
setFill(color.deriveColor(1, 1, 1, 0.5));
setStroke(color);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
}
public void move() {
x += Math.sin( angle) * speed;
y += Math.cos( angle) * speed;
}
public void updateLocation() {
setCenterX( x);
setCenterY( y);
}
}
/**
* Allow movement of objects via mouse.
*/
public class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable( Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
node.setOnMouseReleased(circleOnMouseReleasedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
Particle p = ((Particle) (t.getSource()));
p.selected = true;
orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
}
};
EventHandler<MouseEvent> circleOnMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
Particle p = ((Particle) (t.getSource()));
p.selected = false;
};
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Particle p = ((Particle) (t.getSource()));
p.x = newTranslateX;
p.y = newTranslateY;
}
};
}
}
The update() method in the Spring class is the unknown territory:
public void update() {
double dx = p1.getCenterX() - p2.getCenterX();
double dy = p1.getCenterY() - p2.getCenterY();
double dist = Math.hypot(dx, dy);
double theta = Math.atan2(dy, dx);
double force = (length - dist) * strength;
// System.out.println( dist + ", " + Math.toDegrees( theta) + ", " + force);
// what's supposed to happen here?
p1.angle = ... // <===
p1.speed = ... // <===
p2.angle = ... // <===
p2.speed = ... // <===
}
Here's a screenshot of how it looks like currently:
Thank you very much!
Ok, adding a damping and a non compressable spring:
import java.util.ArrayList;
import java.util.List;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
public class SpringField extends Application {
MouseGestures mg = new MouseGestures();
double damping = 0.995;
double speedo = 0.001;
List<Particle> particles = new ArrayList<>();
List<Spring> springs = new ArrayList<>();
public static void main(String[] args) {
launch(args);
}
Particle addParticle(Group parent, Paint p, double x, double y, double mass) {
Particle particle = new Particle(p, x, y, mass);
mg.makeDraggable(particle);
particles.add(particle);
parent.getChildren().add(particle);
return particle;
}
void addSpring(Group parent, Particle p1, Particle p2, double length, double strength) {
Spring spring = new Spring(parent, p1, p2, length, strength);
springs.add(spring);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
// create particles
Particle pRed = addParticle(root, Color.RED, 300, 100, 10);
Particle pBlue = addParticle(root, Color.BLUE, 600, 200, 1);
Particle pGreen = addParticle(root, Color.GREEN, 300, 300, 1);
// add springs
addSpring(root, pRed, pBlue, 100, 0.5);
addSpring(root, pGreen, pBlue, 100, 0.5);
addSpring(root, pGreen, pRed, 100, 0.5);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
// animate
startAnimation();
}
private void startAnimation() {
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
// move particles
for (Particle p : particles) {
if (!p.selected) {
p.move();
}
}
// apply springs
for (Spring s : springs) {
s.update();
}
// move particles to new location
for (Particle p : particles) {
p.updateLocation();
}
}
};
timer.start();
}
/**
* The spring constraint and calculation. Updates particle
*/
public class Spring {
Particle p1;
Particle p2;
double length; // length it tries to obtain
double strength; // how quickly it tries to reach that length
public Spring(Group parent, Particle p1, Particle p2, double length, double strength) {
this.p1 = p1;
this.p2 = p2;
this.length = length;
this.strength = strength;
Line lineRedBlue = new Line(100, 100, 500, 500);
lineRedBlue.setStroke(Color.BLACK);
lineRedBlue.setStrokeWidth(5);
lineRedBlue.startXProperty().bind(p1.centerXProperty());
lineRedBlue.startYProperty().bind(p1.centerYProperty());
lineRedBlue.endXProperty().bind(p2.centerXProperty());
lineRedBlue.endYProperty().bind(p2.centerYProperty());
parent.getChildren().add(lineRedBlue);
}
public void update() {
double stop = 1.0;
double dx = p1.getCenterX() - p2.getCenterX();
double dy = p1.getCenterY() - p2.getCenterY();
double dist = Math.hypot(dx, dy);
double theta = Math.atan2(dy, dx);
double force = (length - dist) * strength;
if (force > 0) { force *= 4; stop = 0.9; }
// System.out.println( dist + ", " + Math.toDegrees( theta) + ", " + force);
Point2D p1v = new Point2D(force*Math.cos(theta)*speedo/p1.mass, force*Math.sin(theta)*speedo/p1.mass);
Point2D p2v = new Point2D(-force*Math.cos(theta)*speedo/p2.mass, -force*Math.sin(theta)*speedo/p2.mass);
p1.vector = p1.vector.add(p1v).multiply(stop);
p2.vector = p2.vector.add(p2v).multiply(stop);
}
}
/**
* The particle itself
*/
public class Particle extends Circle {
double x;
double y;
Point2D vector = new Point2D(0, 0);
double mass = 1;
boolean selected = false;
public Particle(Paint color, double x, double y, double mass) {
super(x, y, 50);
this.x = x;
this.y = y;
this.mass = mass;
setFill(color);
setStroke(color);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
}
public void move() {
x += vector.getX();
y += vector.getY();
vector = vector.multiply(damping);
}
public void updateLocation() {
setCenterX( x);
setCenterY( y);
}
}
/**
* Allow movement of objects via mouse.
*/
public class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable( Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
node.setOnMouseReleased(circleOnMouseReleasedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
Particle p = ((Particle) (t.getSource()));
p.selected = true;
orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
}
};
EventHandler<MouseEvent> circleOnMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
Particle p = ((Particle) (t.getSource()));
p.selected = false;
};
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Particle p = ((Particle) (t.getSource()));
p.x = newTranslateX;
p.y = newTranslateY;
}
};
}
}
First I would propose to not use angle and speed in your Particle, but Point2D as a movement vector:
public class Particle extends Circle {
double x;
double y;
Point2D vector = new Point2D(0, 0);
that simplifies the later update calulation...
Then you might programm update as follows:
public void update() {
double dx = p1.getCenterX() - p2.getCenterX();
double dy = p1.getCenterY() - p2.getCenterY();
double dist = Math.hypot(dx, dy);
double theta = Math.atan2(dy, dx);
double force = (length - dist) * strength;
// System.out.println( dist + ", " + Math.toDegrees( theta) + ", " + force);
Point2D p1v = new Point2D(force*Math.cos(theta)/p1.mass/10000, force*Math.sin(theta)/p1.mass/10000);
Point2D p2v = new Point2D(-force*Math.cos(theta)/p2.mass/10000, -force*Math.sin(theta)/p2.mass/10000);
p1.vector = p1.vector.add(p1v);
p2.vector = p2.vector.add(p2v);
}
That ignores any collisions, but will provide a fair physics model.
Well here was my approach in a 3D cloth simulation using Verlet Integration:
You can View it here: https://www.youtube.com/watch?v=uRsCcpbsdsg
/**
*
* #author Jason Pollastrini aka jdub1581
*/
#FunctionalInterface
public interface Constraint {
public void solve();
public default void solve(int iter){
IntStream.range(0, iter).parallel().forEach(i->{solve();});
}
}
Then in implementing class (I called it PointLink):
public void solve() {
// calculate the distance between the two PointMasss
Point3D diff = new Point3D(
p1.getPosition().getX() - p2.getPosition().getX(),
p1.getPosition().getY() - p2.getPosition().getY(),
p1.getPosition().getZ() - p2.getPosition().getZ()
);
double d = diff.magnitude();
double difference = (distance - d) / d;
double im1 = 1 / p1.getMass();
double im2 = 1 / p2.getMass();
double scalarP1 = (im1 / (im1 + im2)) * stiffness;
double scalarP2 = stiffness - scalarP1;
p1.position.x = (float) (p1.getPosition().x + diff.x * scalarP1 * difference);
p1.position.y = (float) (p1.getPosition().y + diff.y * scalarP1 * difference);
p1.position.z = (float) (p1.getPosition().z + diff.z * scalarP1 * difference);
p2.position.x = (float) (p2.getPosition().x - diff.x * scalarP2 * difference);
p2.position.y = (float) (p2.getPosition().y - diff.y * scalarP2 * difference);
p2.position.z = (float) (p2.getPosition().z - diff.z * scalarP2 * difference);
}
Then in the Main class something like this:
public void solveConstraints() {
constraints.values().parallelStream().forEach((Constraint c) -> {
c.solve();
});
}
public void updatePhysics(double dt, double t) {
if (isAnchored()) {
setPosition(getAnchorPosition());
return;
}
Point3D vel = new Point3D(
(position.x - oldPosition.x),
(position.y - oldPosition.y),
(position.z - oldPosition.z)
);
float dtSq = (float) (dt * dt);
// calculate the next position using Verlet Integration
Point3D next = new Point3D(
position.x + vel.x + (((force.x / (float) (mass)) * 0.5f) * dtSq),
position.y + vel.y + (((force.y / (float) (mass)) * 0.5f) * dtSq),
position.z + vel.z + (((force.z / (float) (mass)) * 0.5f) * dtSq)
);
// reset variables
setOldPosition(position);
setPosition(next);
setForce(new Point3D(0, 0, 0));
}
Note that I used a simple class with double x,y,z as public members for ease of use for position variable...

JavaFX 3-D Bar chart with Java 8

Is there any example of 3-D Bar chart for the latest Java 8 which uses the modern 3-D API?
I would like to use the 3-D API from Java 8.
There was a 3d-Bar-Chart demo in the earlier versions of JavaFX, but that was removed.
You can create a bar chart yourself. Just create a grid and some boxes on it. Here's a slight modification of the answer in How to create a 3d / surface chart with JavaFX. Drag around via mouse, use mouse wheel to zoom.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class BarChart3dDemo extends Application {
private static Random rnd = new Random();
// size of graph
int graphSize = 400;
// variables for mouse interaction
private double mousePosX, mousePosY;
private double mouseOldX, mouseOldY;
private final Rotate rotateX = new Rotate(20, Rotate.X_AXIS);
private final Rotate rotateY = new Rotate(-45, Rotate.Y_AXIS);
#Override
public void start(Stage primaryStage) {
// create axis walls
Group grid = createGrid(graphSize);
// initial cube rotation
grid.getTransforms().addAll(rotateX, rotateY);
// add objects to scene
StackPane root = new StackPane();
root.getChildren().add(grid);
// create bars
double gridSizeHalf = graphSize / 2;
double size = 30;
for (double i = -gridSizeHalf + size; i < gridSizeHalf; i += 50) {
for (double j = -gridSizeHalf + size; j < gridSizeHalf; j += 50) {
double height = rnd.nextDouble() * 300;
Box box = new Box(size, height, size);
// color
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseColor(randomColor());
box.setMaterial(mat);
// location
box.setLayoutY(-height * 0.5 + graphSize * 0.5);
box.setTranslateX(i);
box.setTranslateZ(j);
grid.getChildren().addAll(box);
}
}
// scene
Scene scene = new Scene(root, 1600, 900, true, SceneAntialiasing.BALANCED);
scene.setCamera(new PerspectiveCamera());
scene.setOnMousePressed(me -> {
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
});
scene.setOnMouseDragged(me -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
mouseOldX = mousePosX;
mouseOldY = mousePosY;
});
makeZoomable(root);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Axis wall
*/
public static class Axis extends Pane {
Rectangle wall;
public Axis(double size) {
// wall
// first the wall, then the lines => overlapping of lines over walls
// works
wall = new Rectangle(size, size);
getChildren().add(wall);
// grid
double zTranslate = 0;
double lineWidth = 1.0;
Color gridColor = Color.WHITE;
for (int y = 0; y <= size; y += size / 10) {
Line line = new Line(0, 0, size, 0);
line.setStroke(gridColor);
line.setFill(gridColor);
line.setTranslateY(y);
line.setTranslateZ(zTranslate);
line.setStrokeWidth(lineWidth);
getChildren().addAll(line);
}
for (int x = 0; x <= size; x += size / 10) {
Line line = new Line(0, 0, 0, size);
line.setStroke(gridColor);
line.setFill(gridColor);
line.setTranslateX(x);
line.setTranslateZ(zTranslate);
line.setStrokeWidth(lineWidth);
getChildren().addAll(line);
}
}
public void setFill(Paint paint) {
wall.setFill(paint);
}
}
public void makeZoomable(StackPane control) {
final double MAX_SCALE = 20.0;
final double MIN_SCALE = 0.1;
control.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double delta = 1.2;
double scale = control.getScaleX();
if (event.getDeltaY() < 0) {
scale /= delta;
} else {
scale *= delta;
}
scale = clamp(scale, MIN_SCALE, MAX_SCALE);
control.setScaleX(scale);
control.setScaleY(scale);
event.consume();
}
});
}
/**
* Create axis walls
*
* #param size
* #return
*/
private Group createGrid(int size) {
Group cube = new Group();
// size of the cube
Color color = Color.LIGHTGRAY;
List<Axis> cubeFaces = new ArrayList<>();
Axis r;
// back face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.5 * 1), 1.0));
r.setTranslateX(-0.5 * size);
r.setTranslateY(-0.5 * size);
r.setTranslateZ(0.5 * size);
cubeFaces.add(r);
// bottom face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.4 * 1), 1.0));
r.setTranslateX(-0.5 * size);
r.setTranslateY(0);
r.setRotationAxis(Rotate.X_AXIS);
r.setRotate(90);
cubeFaces.add(r);
// right face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.3 * 1), 1.0));
r.setTranslateX(-1 * size);
r.setTranslateY(-0.5 * size);
r.setRotationAxis(Rotate.Y_AXIS);
r.setRotate(90);
// cubeFaces.add( r);
// left face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.2 * 1), 1.0));
r.setTranslateX(0);
r.setTranslateY(-0.5 * size);
r.setRotationAxis(Rotate.Y_AXIS);
r.setRotate(90);
cubeFaces.add(r);
// top face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.1 * 1), 1.0));
r.setTranslateX(-0.5 * size);
r.setTranslateY(-1 * size);
r.setRotationAxis(Rotate.X_AXIS);
r.setRotate(90);
// cubeFaces.add( r);
// front face
r = new Axis(size);
r.setFill(color.deriveColor(0.0, 1.0, (1 - 0.1 * 1), 1.0));
r.setTranslateX(-0.5 * size);
r.setTranslateY(-0.5 * size);
r.setTranslateZ(-0.5 * size);
// cubeFaces.add( r);
cube.getChildren().addAll(cubeFaces);
return cube;
}
public static double normalizeValue(double value, double min, double max, double newMin, double newMax) {
return (value - min) * (newMax - newMin) / (max - min) + newMin;
}
public static double clamp(double value, double min, double max) {
if (Double.compare(value, min) < 0)
return min;
if (Double.compare(value, max) > 0)
return max;
return value;
}
public static Color randomColor() {
return Color.rgb(rnd.nextInt(255), rnd.nextInt(255), rnd.nextInt(255));
}
public static void main(String[] args) {
launch(args);
}
}
Refer to this example, it shows use of 3-D bar chart if it helps you.
http://www.jroller.com/dgilbert/entry/creating_3d_charts_in_java
Jzy3d has nice bar charts in its gallery. You can use Jzy3d with JavaFX easily.

Resources