Я занимался разработкой университетского проекта для построения решения дифференциального уравнения, названного моделью Лотки-Вольтерри.Речь идет об отношениях между жертвами и хищниками в природе.Окно отображается нормально, когда я нажимаю любую из кнопок, программа выдает исключение в консоли.Я пытался выяснить самостоятельно, что идет не так в течение 2 часов безрезультатно.
Класс для создания данных работает по назначению, я создал csv-generator и построил его в Excel.
Вот мой класс контроллера:
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math3.ode.FirstOrderIntegrator;
import org.apache.commons.math3.ode.nonstiff.EulerIntegrator;
import java.util.ArrayList;
public class Controller {
private double aHolder = -1;
private double bHolder = -1;
private double cHolder = -1;
private double dHolder = -1;
//datasets
private XYChart.Series XT = new XYChart.Series();
private XYChart.Series VT = new XYChart.Series();
private XYChart.Series VX = new XYChart.Series();
@FXML
private TextField textA;
@FXML
private TextField textB;
@FXML
private TextField textC;
@FXML
private TextField textD;
@FXML
private Button plotXT;
@FXML
private Button plotVT;
@FXML
private Button plotVX;
@FXML
private LineChart<Number, Number> figure;
@FXML
private NumberAxis t;
@FXML
private NumberAxis x;
@FXML
void onclickPlotVT(ActionEvent event) {
boolean isTheSame = checkIfTheSameAndSetIfNot();
if(!isTheSame){
calculate();
}
figure.getData().clear();
figure.getData().add(VT);
}
@FXML
void onclickPlotVX(ActionEvent event) {
boolean isTheSame = checkIfTheSameAndSetIfNot();
if(!isTheSame){
calculate();
}
figure.getData().clear();
figure.getData().add(VX);
}
@FXML
void onclickPlotXT(ActionEvent event) {
boolean isTheSame = checkIfTheSameAndSetIfNot();
if(!isTheSame){
calculate();
}
figure.getData().clear();
figure.getData().add(XT);
}
//if
private boolean checkIfTheSameAndSetIfNot(){
double currentA = Double.parseDouble(textA.getText());
double currentB = Double.parseDouble(textB.getText());
double currentC = Double.parseDouble(textC.getText());
double currentD = Double.parseDouble(textD.getText());
boolean checkA = currentA == aHolder;
boolean checkB = currentB == bHolder;
boolean checkC = currentC == cHolder;
boolean checkD = currentD == dHolder;
//if all parameters are the same, return true, other way false
if(checkA && checkB && checkC && checkD){
return true;
}
aHolder = currentA;
bHolder = currentB;
cHolder = currentC;
dHolder = currentD;
return false;
}
private void calculate(){
FirstOrderDifferentialEquations diffEquSolver = new DiffEquSolverZad2(aHolder, bHolder, cHolder, dHolder);
FirstOrderIntegrator diffEquLIntegrator = new EulerIntegrator(0.01);
DiffEquSolverPath diffSolverEquPath = new DiffEquSolverPath();
double [] xStart = new double [] {0,0};
double [] xStop = new double [] {10,10};
diffEquLIntegrator.addStepHandler(diffSolverEquPath);
diffEquLIntegrator.integrate(diffEquSolver,0,xStart,20,xStop);
ArrayList<String> csv = diffSolverEquPath.getCsv();
for(String s : csv){
String data[] = s.split(",");
XT.getData().add(new XYChart.Data(Double.parseDouble(data[0]), Double.parseDouble(data[1])));
VT.getData().add(new XYChart.Data(Double.parseDouble(data[0]), Double.parseDouble(data[2])));
VX.getData().add(new XYChart.Data(Double.parseDouble(data[1]), Double.parseDouble(data[2])));
}
System.out.println();
}
}
Основной класс содержит только создание и ополаскивание сцены, созданной SceneBuilder.
Позиция стека после создания исключения выглядит следующим образом:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String (java.lang.Double and java.lang.String are in module java.base of loader 'bootstrap')
at javafx.controls/javafx.scene.chart.CategoryAxis.invalidateRange(CategoryAxis.java:456)
at javafx.controls/javafx.scene.chart.LineChart.updateAxisRange(LineChart.java:207)
at javafx.controls/javafx.scene.chart.XYChart.layoutChartChildren(XYChart.java:670)
at javafx.controls/javafx.scene.chart.Chart$1.layoutChildren(Chart.java:95)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1204)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2482)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:519)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:499)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:492)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:320)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:834)
Другие классы (работают по назначению):
package sample;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.MaxCountExceededException;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
public class DiffEquSolverZad2 implements FirstOrderDifferentialEquations {
private double a;
private double b;
private double c;
private double d;
public DiffEquSolverZad2(double a, double b, double c, double d) {
if(a > 0 && b > 0 && c > 0 && d > 0) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
} else {
throw new IllegalArgumentException("All parameters must be greater than 0. ");
}
}
@Override
public int getDimension() {
return 2;
}
@Override
public void computeDerivatives(double t, double[] x, double[] dxdt) throws MaxCountExceededException, DimensionMismatchException {
dxdt[0]= (a-b*x[1])*x[0];
dxdt[1]= (c*x[0]-d)*x[1];
}
}
package sample;
import org.apache.commons.math3.exception.MaxCountExceededException;
import org.apache.commons.math3.ode.sampling.StepHandler;
import org.apache.commons.math3.ode.sampling.StepInterpolator;
import java.util.ArrayList;
import java.util.Arrays;
public class DiffEquSolverPath implements StepHandler {
private ArrayList<String> csv = new ArrayList<>();
private ArrayList<Double> tValues = new ArrayList<>();
public ArrayList<String> getCsv() {
return csv;
}
public void clearCSVArray(){
csv.clear();
}
@Override
public void init(double v, double[] doubles, double v1) {
}
@Override
public void handleStep(StepInterpolator stepInterpolator, boolean b) throws MaxCountExceededException {
double t=stepInterpolator.getCurrentTime();//czas dla którego otrzymaliśmy rozwiązanie numeryczne(obliczono wartości xi v)
double [] x=stepInterpolator.getInterpolatedState(); //wartości x i v
csv.add(t+", "+ Arrays.toString(x).replace("[", "").replace("]", ""));
tValues.add(t);
}
}
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Zad5 from L5 Numeric Methods. ");
primaryStage.setScene(new Scene(root, 1000, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Файл FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="389.0" prefWidth="933.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<opaqueInsets>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</opaqueInsets>
<top>
<VBox prefHeight="78.0" prefWidth="563.0" BorderPane.alignment="CENTER">
<children>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="20.0">
<children>
<Label prefHeight="50.0" prefWidth="119.0" text="Model Lotki-Volterry">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<Label text="a:" />
<TextField fx:id="textA" promptText="1" />
<Label text="b:" />
<TextField fx:id="textB" promptText="1" />
<Label text="c:" />
<TextField fx:id="textC" promptText="1" />
<Label text="d:" />
<TextField fx:id="textD" promptText="1" />
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="30.0">
<children>
<VBox alignment="CENTER" prefHeight="39.0" prefWidth="286.0">
<children>
<Label text="a – współczynnik przyrostu ofiar" />
<Label text="b – częstość umierania ofiar na skutek drapieżnictwa" />
</children>
</VBox>
<VBox alignment="CENTER" prefHeight="39.0" prefWidth="266.0">
<children>
<Label text="c – współczynnik przyrostu drapieżników" />
<Label text="d – częstość umierania drapieżników" />
</children>
</VBox>
<Button fx:id="plotXT" mnemonicParsing="false" onAction="#onclickPlotXT" text="Wykres x(t)" />
<Button fx:id="plotVT" mnemonicParsing="false" onAction="#onclickPlotVT" text="Wykres v(t)" />
<Button fx:id="plotVX" mnemonicParsing="false" onAction="#onclickPlotVX" text="Wykres v(x)" />
</children>
</HBox>
</children>
</VBox>
</top>
<center>
<LineChart fx:id="figure" prefHeight="249.0" prefWidth="563.0" BorderPane.alignment="CENTER">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
</center>
</BorderPane>