package gl8080.physics.view.shape;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import gl8080.physics.domain.Physical;
import gl8080.physics.domain.PhysicalLaw;
//import gl8080.physics.domain.primitive.Point;
import gl8080.physics.view.Content;
import gl8080.physics.view.ViewPoint;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Sphere;
public class BallLocus implements Content, PhysicalLaw {
private int historySzie;
private Color color;
private double radius;
private int interval;
private PhysicalLaw law;
private Group group = new Group();
private Map<Physical, Deque<Sphere>> spheresMap = new HashMap<>();
private Map<Physical, Integer> countMap = new HashMap<>();
public static BallLocusBuilder create(PhysicalLaw law) {
return new BallLocusBuilder(law);
}
@Override
public Node getNode() {
return this.group;
}
@Override
public void apply(Physical ball, double sec) {
int count = this.countMap.getOrDefault(ball, 0) + 1;
if (count % this.interval == 0) {
this.drawLocus(ball);
count = 0;
}
this.countMap.put(ball, count);
this.law.apply(ball, sec);
}
private void drawLocus(Physical ball) {
// Main スレッド以外で UI を操作するとエラーになるので、 Platform.runLater() を使って UI スレッドで処理を実行する
Platform.runLater(() -> {
Sphere sphere = this.createSphere(ball);
Deque<Sphere> spheres = this.spheresMap.computeIfAbsent(ball, (key) -> new ArrayDeque<>());
spheres.addLast(sphere);
this.group.getChildren().add(sphere);
if (this.historySzie < spheres.size()) {
Sphere removed = spheres.removeFirst();
this.group.getChildren().remove(removed);
}
});
}
private Sphere createSphere(Physical ball) {
Sphere sphere = new Sphere(this.radius);
ViewPoint location = ViewPoint.of(ball.getLocation());
sphere.setTranslateX(location.x);
sphere.setTranslateY(location.y);
sphere.setTranslateZ(location.z);
Material material = new PhongMaterial(this.color);
sphere.setMaterial(material);
return sphere;
}
private BallLocus() {}
public static class BallLocusBuilder {
private BallLocus locus = new BallLocus();
public BallLocusBuilder(PhysicalLaw law) {
Objects.requireNonNull(law);
this.locus.law = law;
this.locus.radius = 0.5;
this.locus.color = Color.YELLOW;
this.locus.historySzie = 50;
this.locus.interval = 10;
}
public BallLocusBuilder radius(double radius) {
if (radius < 0.0) {
throw new IllegalArgumentException();
}
this.locus.radius = radius;
return this;
}
public BallLocusBuilder color(Color color) {
Objects.requireNonNull(color);
this.locus.color = color;
return this;
}
public BallLocusBuilder interval(int interval) {
if (interval <= 0) {
throw new IllegalArgumentException();
}
this.locus.interval = interval;
return this;
}
public BallLocusBuilder historySize(int historySize) {
if (historySize <= 0) {
throw new IllegalArgumentException();
}
this.locus.historySzie = historySize;
return this;
}
public BallLocus build() {
return this.locus;
}
}
@Override
public void refresh() {
// ignore
}
}