2014-11-11 2 views
1

Я попытался создать похожую на весну поведение с JavaFX, создав перетаскиваемые круги. Когда я перетаскиваю один круг, остальные должны следовать и моделировать эластичность.Весна физики JavaFX

Я создал шаблон шаблона, который содержит 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; 
      } 
     }; 

    } 
} 

Метод обновления() в классе Spring является неизвестной территорией:

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 = ... // <=== 
} 

Вот скриншот того, как она выглядит в настоящее время:

enter image description here

спасибо!

ответ

1

Хорошо, добавляя демпфирование и нон сжимаемой пружины:

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

    } 
} 
+0

Пружинная остановка как-то взламывает здесь ... не на 100% физически правильно ... только подсказка ... :-D –

+0

THANK ВЫ ОЧЕНЬ МНОГО за вашу помощь! Очень ценно :-) – Roland

+0

Это также помогло бы, по моему мнению, сделать несколько итераций за обновление .. for (int a = 0; a <точность; a ++;) {solveConstraints(); }, в конечном итоге это приведет к более реалистичному моделированию. – jdub1581

1

Сначала я хотел бы предложить, чтобы не использовать угол и скорость вашей частицы, но Point2D как вектор движения:

public class Particle extends Circle { 

    double x; 
    double y; 

    Point2D vector = new Point2D(0, 0); 

, что упрощает обновление позже calulation ...

Затем вы можете программку обновления следующим образом:

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

Это игнорирует любые столкновения, но обеспечит справедливую физическую модель.

+0

Конечно, вы можете добавить демпфирование и коллизии также ;-) –

+0

Это было быстро, спасибо вам большое! :-) Однако он не работает должным образом. Круги перемещаются все время и не ограничиваются «длиной», на которую я их устанавливал. Я просто заметил, что в своем коде я ограничил их до 10, что должно быть 100. Но, спасибо вам большое, но я не заметил, что Point2D будет иметь такие удобные методы. Я узнаю, что JavaFX потрясающе изо дня в день. – Roland

+0

ok, длина пружины здесь не является настоящим жестким пределом, что зависит от типа пружины, который вы хотите эмулировать ... если пружина не «сжимается», тогда обновление нуждается в некоторой модификации ...как уже упоминалось, демпфирование должно быть добавлено для замедления и остановки .... –

0

Ну вот был мой подход в 3D моделирования ткани с использованием Верле интеграции:

Вы можете просмотреть его здесь: 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();}); 
     } 
    } 

Затем в классе реализации (я назвал его 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); 
} 

Затем в главном классе что-то вроде этого:

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

} 

Обратите внимание, что я использовал простой класс с двойными х, у, г, как представители общественности для простоты использования переменной позиции ...

+0

Поверхность интерфейса - это то, что вы можете легко добавить и другие ограничения. В моей одежде я использую 3 разных типа пружин. Сдвиг, изгиб и стретч, все с разными значениями. – jdub1581

Смежные вопросы