JavaFX弹簧物理学

JavaFX弹簧物理学,javafx,physics,Javafx,Physics,我试图通过创建可拖动的圆圈,用JavaFX创建一个类似spring的行为。当我拖动一个圆圈时,其他圆圈应跟随并模拟弹性 我创建了一个样板模板,其中包含3个圆圈,它们可以通过鼠标拖动。动画正在运行,当然,所有动画都显示为静止,因为当前速度为0。我只需要知道如何计算附着粒子的角度和速度 如果有人能帮助我,那就太好了 代码如下: import java.util.ArrayList; import java.util.List; import javafx.animation.AnimationTi

我试图通过创建可拖动的圆圈,用JavaFX创建一个类似spring的行为。当我拖动一个圆圈时,其他圆圈应跟随并模拟弹性

我创建了一个样板模板,其中包含3个圆圈,它们可以通过鼠标拖动。动画正在运行,当然,所有动画都显示为静止,因为当前速度为0。我只需要知道如何计算附着粒子的角度和速度

如果有人能帮助我,那就太好了

代码如下:

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;
            }
        };

    }
}
import java.util.ArrayList;
导入java.util.List;
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.event.EventHandler;
导入javafx.scene.Group;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Circle;
导入javafx.scene.shape.Line;
导入javafx.scene.shape.StrokeType;
导入javafx.stage.stage;
公共类PhysicTest扩展了应用程序{
列表粒子=新的ArrayList();
列表弹簧=新的ArrayList();
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
组根=新组();
//创建粒子
粒子pRed=新粒子(颜色:红色,100100);
粒子pBlue=新粒子(颜色:蓝色,400200);
粒子绿色=新粒子(颜色为绿色,100300);
//红色->蓝色
lineRedBlue=新行(100100500500);
lineRedBlue.setStroke(颜色:黑色);
lineRedBlue.设置行程宽度(5);
//绿色->蓝色
lineGreenBlue=新线(100100500500);
线条绿蓝色。设定行程(颜色。黑色);
蓝绿色线条。设置行程宽度(5);
//线装订
//生产线1->2
lineRedBlue.startXProperty().bind(pRed.centerXProperty());
lineRedBlue.startYProperty().bind(pRed.centerYProperty());
lineRedBlue.endXProperty().bind(pBlue.centerXProperty());
lineRedBlue.endYProperty().bind(pBlue.centerYProperty());
//第3行->第2行
lineGreenBlue.startXProperty().bind(pGreen.centerXProperty());
lineGreenBlue.startYProperty().bind(pGreen.centerYProperty());
lineGreenBlue.endXProperty().bind(pBlue.centerXProperty());
lineGreenBlue.endYProperty().bind(pBlue.centerYProperty());
MouseGestures mg=新的MouseGestures();
mg.makeDraggable(pRed);
mg.makeDraggable(pBlue);
mg.makeDraggable(pGreen);
root.getChildren().addAll(pRed、pBlue、pGreen、lineRedBlue、lineGreenBlue);
//添加到列表中
粒子。添加(pRed);
添加颗粒(pBlue);
粒子。添加(绿色);
//添加弹簧
弹簧s1=新弹簧(pRed,pBlue,10,0.5);
弹簧。添加(s1);
弹簧s2=新弹簧(绿色,蓝色,10,0.5);
弹簧。添加(s2);
设置场景(新场景(根,1024768));
primaryStage.show();
//生动活泼
startAnimation();
}
私有void startAnimation(){
AnimationTimer=新的AnimationTimer(){
@凌驾
公共无效句柄(长){
//移动粒子
对于(粒子p:粒子){
如果(!p.selected){
p、 move();
}
}
//施加弹簧
用于(弹簧s:弹簧){
s、 更新();
}
//将粒子移动到新位置
对于(粒子p:粒子){
p、 updateLocation();
}
}
};
timer.start();
}
/**
*弹簧约束和计算。更新粒子
*/
公共课春季{
粒子p1;
颗粒p2;
double length;//它尝试获取的长度
双倍力量;//它达到这个长度的速度有多快
公共弹簧(粒子p1、粒子p2、双倍长度、双倍强度){
这是1.p1=p1;
这是p2=p2;
这个长度=长度;
力量=力量;
}
公共无效更新(){
双dx=p1.getCenterX()-p2.getCenterX();
双dy=p1.getCenterY()-p2.getCenterY();
双距离=数学形波(dx,dy);
双θ=Math.atan2(dy,dx);
双力=(长度-距离)*强度;
//System.out.println(dist+“,”+数学toDegrees(θ)+“,”+力);
//这里会发生什么?

p1.angle=…/首先,我建议不要在粒子中使用角度和速度,而是使用Point2D作为运动矢量:

public class Particle extends Circle {

    double x;
    double y;

    Point2D vector = new Point2D(0, 0);
这简化了以后的更新计算

然后您可以按如下方式进行程序更新:

    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);
    }

这忽略了任何碰撞,但将提供一个公平的物理模型。

好的,添加一个阻尼和一个不可压缩的弹簧:

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;
            }
        };

    }
}
import java.util.ArrayList;
导入java.util.List;
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.event.EventHandler;
导入javafx.geometry.Point2D;
导入javafx.scene.Group;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.paint.Color;
导入javafx.scene.paint.paint;
导入javafx.scene.shape.Circle;
导入javafx.scene.shape.Line;
导入javafx.scene.shape.StrokeType;
导入javafx.stage.stage;
公共类SpringField扩展了应用程序{
MouseGestures mg=新的MouseGestures();
双阻尼=0.995;
双speedo=0.001;
列表粒子=新的ArrayList();
列表弹簧=新的ArrayList();
公共静态void main(字符串[]args){
发射(args);
}
粒子添加粒子(组父对象、绘制p、双x、双y、双质量){
粒子
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;
            }
        };

    }
}
/**
*
* @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();});
      }
  }
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);
}
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));

}