JavaFX
Advertisement

Главная | Описание языка | FXD | API | Примеры | Инструменты Разработки | Новости | Ресурсы | Форум

Разлет частиц[]

Релятивистский случай[]

Стартовое изображение прграммы

Треугольник в геометрии Лобачевского

Теорема косинусов в геометрии Лобачевского:

 

Вторая теорема косинусов:

 

Карта скоростей для разлета 2х частиц

Столкновение частиц. Расчеты проводятся на карте скоростей процесса. Таким образм, если отвечает длине вектора скорости на карте в системе центра масс, то выполнены следущие равенства:

 , импульс частицы,
 , энергия,
 , скорость часицы, относительно скорости света.
 

Закон сохранения импульса и энергии:

 
 

Из этих законов можно заметить, что v1=v1' и v2=v2'.

Релятивистская формула сложения скоростей:

 , где с - скорость света.

В лабораторной системе отсчета известна только скорость налетающей частицы-AB, с помощью масс можно найти скорости v1 и v2, для этого будем использовать 3 формулы: если AO обозначить за a, OB за b, а AB за c (c=a+b), то v1=th\ a, v2=th\ b, и в системе центра масс импульс системы равен 0, т.е. модули импульсов равны, откуда

 (релятивисткое правило рычага), т.е.  

Далее рассмотрим формулы синуса и косинуса суммы в гиперболической тригонометрии:



выразим из второй формулы ch b:


подставляем это выражение в первую формулу, заменяем sh b по правилу рычага:





Аналогично

Далее, по теореме косинусов можно найти скорости отлета c и c' и углы при них в лабораторной системе отсчета и :

Отсюда получаем скорость в координатной системе:

Полная программа:


import javafx.animation.*;
import javafx.application.*;
import javafx.ext.swing.*;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.geometry.*;
import javafx.scene.transform.*;
import javafx.scene.text.*;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import java.lang.Math;
import java.lang.*;
import java.lang.System;
import javafx.input.*;
import javafx.input.MouseEvent;


var disang:Number;
var r=0;
var nu=1;
var h=6.6256e-34;
var c=299792458;
var v = 0.11;//скорость налетающей частицы относительно скорости света
var sp=0.0 ;
var l=2;
var protonMass = 0.1;
var electronMass = 0.01;
var tests = [
    TestCase{
        angle: 0.0
        result1: Particle{
            name: "Result Proton 1"
            angle: 0.0
            speed: 0.0
        }
        result2: Particle{
            name: "Result Proton 2"
            angle: 0.0
            speed: 0.5
        }
    }
    
];
var timeline = Timeline {
        keyFrames:  KeyFrame { time: 0.01s,  action: function() { particleSystem.run() 
        } } 
    repeatCount: java.lang.Double.POSITIVE_INFINITY
} 
var particleSystem = ParticleSystem{
    transform: Transform.translate(400, 300)
            
    particles: [
        Particle{
            name: "Particle 1"
            mass: 10
            radius: 4
            startx: -146
            starty: 0            
            speed: 0.5
            color: Color.GREEN
            
        },
        Particle{
            name: "Particle 2"
            mass: 10
            radius: 4
            startx: 0
            starty: 0
            speed: 0
            color: Color.RED
            
        }
    ]
           
};

function abs (x:Number):Number {
    return if ( 0 < x ) then x else -x;
} 
function q (w:Number):Number{//возведение в квадрат
    return w*w
}
function arth (x:Number):Number{//обратная к гиперболическому тангенсу
    return if (x==1) 1
    else  Math.log((1+x)/(1-x))/2
}
function arch (x:Number):Number{//~косинуа
    return Math.log(x+Math.sqrt(q(x)-1)) 
}
function arsh (x:Number):Number{//~синуса
    return Math.log(x+Math.sqrt(q(x)+1))
}
function tch (ang:Number, v1:Number, v2:Number):Number{//по теореме косинусов находит сторону по сторонам и углу
   return if (ang==0) Math.tanh(abs(arth(v1)-arth(v2))) 
   else if (ang==Math.PI) (v1+v2)/(1+v1*v2)
      else   round(Math.tanh(arch((Math.cosh(arth(v1))*Math.cosh(arth(v2))-Math.sinh(arth(v1))*Math.sinh(arth(v2))*Math.cos(ang)))),3);
 }
function tch1 (v1:Number, v2:Number, v3:Number):Number{//по теореме косинусов находим угол разлета
    return if (v3==0) 0
    else if (v2==0) 0
    else if (v1==0) 0
    else round(Math.acos((Math.cosh(arth(v1))*Math.cosh(arth(v2))-Math.cosh(arth(v3)))/Math.sinh(arth(v1))/Math.sinh(arth(v2))),3)
}
function round (N:Number,n:Number):Number{
    var a=1;
    for (i in [1..n]) {a=a*10;  }
    var b=N*a;
  b=Math.round(b);
  return b/a
}
function sub(p1:Particle, angle:Number,speed:Number){
    
    var b1=p1.speed*Math.cos(p1.angle);
    System.out.println('b1='+b1);
    var b2=speed*Math.cos(angle);
    System.out.println('b2='+b2);
    System.out.println(speed);
    System.out.println(angle);
    var b=(b2-b1)/(1-b1*b2);
    System.out.println('b='+b);
    
    
    var a1=Math.tanh(arsh(Math.sinh(arth(p1.speed))*Math.sin(p1.angle)));
    var a2=Math.tanh(arsh(Math.sinh(arth(speed))*Math.sin(angle)));
    var a=(a2-a1)/(1-a1*a2);
    System.out.println('a='+ a);
    p1.speed=tch(Math.PI/2,a,b);
    System.out.println('ps='+ p1.speed);
    p1.angle=Math.atan(a/Math.sinh(arth(b)));
    System.out.println('pa='+ p1.angle);
    if (b<0)p1.angle=Math.PI+p1.angle;
   // if (a>0){if (b>0) p1.angle=abs(p1.angle) else }
}
/*function sub(p1:Particle, angle:Number,speed:Number){
    var pspeed=p1.speed;
    p1.speed=tch(p1.angle-angle,p1.speed,speed);
    if (p1.angle>angle){
        if(p1.angle-angle<=Math.PI){
            System.out.println("***1***"); 
            p1.angle=angle+tch1(speed,p1.speed,pspeed);
        }
        else {
            System.out.println("***2***"); 
            p1.angle=angle-tch1(speed,p1.speed,pspeed);
        }
    }
    else {
        if (angle-p1.angle<=Math.PI){
            System.out.println("***3***"); 
            p1.angle=angle-tch1(speed,p1.speed,pspeed);
     }
        else{
            System.out.println("***4***"); 
            p1.angle=angle+tch1(speed,p1.speed,pspeed);
     } 
}
     System.out.println(p1.angle);
     System.out.println(p1.speed);
     System.out.println(pspeed);
  }*/
function line (p1:Particle,p2:Particle, ang:Number){
    System.out.println("angel1="+p1.angle);
    System.out.println("2="+p2.angle);
    p1.angle=ang;
    p2.angle=Math.PI+ang;
    System.out.println("angel="+ang)
    
}
function line_for_foton (p1:Particle, p2:Particle, ang:Number){
    if (p1.speed==1){
        if (not(p2.speed==1)) p1.angle=Math.random()*Math.PI*2
    }
else p2.angle=Math.random()*Math.PI*2    
}
function start (p1:Particle,p2:Particle, ang:Number){
    if (p1.angle==p2.angle) v=(p1.speed+p2.speed)/(1+p1.speed*p2.speed)
    else
    v=tch(p1.angle-p2.angle,p1.speed,p2.speed);
       System.out.println("v="+v); 
    var v1=p2.mass*Math.sinh(arth(v))/(p1.mass+p2.mass*Math.cosh(arth(v)));
       
       System.out.println("v1="+v1); 
    var v2=p1.mass*Math.sinh(arth(v))/(p2.mass+p1.mass*Math.cosh(arth(v)));
       System.out.println("v2="+v2); 
    var speed=tch(tch1(v,p1.speed,p2.speed),v1,p1.speed);
       System.out.println("speed="+speed); 
    var angle;
    var dopang;
    var pang;
    if (p1.speed==0){pang=p2.angle;} else pang=p1.angle;
    if (p1.angle>p2.angle){//пока правильно-угол е-8
        
        if(p1.angle-p2.angle<=Math.PI){
            angle=p1.angle-tch1(p1.speed,speed,v1); 
            //dopang=p1.angle-tch1(p1.speed,v,p2.speed);  
            System.out.println("***1***"); 
             System.out.println("p2.angle="+p2.angle);
            System.out.println(p1.speed); 
            System.out.println(speed);
            System.out.println(v1);
            System.out.println(tch1(p1.speed,speed,v1)); 
        }
        else {
            angle=p1.angle+tch1(p1.speed,speed,v1); 
            //dopang=p2.angle-tch1(p2.speed,v,p1.speed);  
            System.out.println("***2***"); 
             System.out.println("p2.angle="+p2.angle);
            System.out.println(p1.speed); 
            System.out.println(speed);
            System.out.println(v1);
            System.out.println(tch1(p1.speed,speed,v1)); 
        }
    }
    else {
        if (p2.angle-p1.angle<=Math.PI){
            angle=pang+tch1(p1.speed,speed,v1); 
            //dopang=p2.angle-tch1(p2.speed,v,p1.speed);  
            System.out.println("***3***"); 
            System.out.println("p2.angle="+p2.angle); 
            System.out.println("dop="+dopang); 
            System.out.println(p1.speed); 
            System.out.println(speed);
            System.out.println(v1);
            System.out.println(tch1(p1.speed,speed,v1)); 
     }
        else{
            angle=pang-tch1(p1.speed,speed,v1); 
            //dopang=p1.angle-tch1(p1.speed,v,p2.speed);  
            System.out.println("***4***"); 
             System.out.println("p2.angle="+p2.angle);
            System.out.println(p1.speed); 
            System.out.println(speed);
            System.out.println(v1);
            System.out.println(tch1(p1.speed,speed,v1)); 
     } 
     
     
      
};
   System.out.println("angle="+angle); 
   { var p1s=p1.speed;
     var p1a=p1.angle;
     sub (p1,p2.angle,p2.speed);
     dopang=p1.angle;
     System.out.println("dopang="+dopang);
     System.out.println("v="+p2.speed);
     p1.speed=p1s;
     p1.angle=p1a;}
    p1.speed=v1; 
    p2.speed=v2;
    p1.angle=0;
    p2.angle=0;
    line(p1,p2,ang);
     p1.angle=p1.angle+dopang;
    p2.angle=p2.angle+dopang;
     System.out.println("p1.angle "+p1.angle);
     System.out.println("p2.angle "+p2.angle);
     System.out.println("p1.speed "+p1.speed);
     System.out.println("p2.speed "+p2.speed);
    
    sub(p1,angle,speed);
    sub(p2,angle,speed);
      System.out.println("1 "+p1.angle);
     System.out.println("2 "+p2.angle);
   
 
 
 
 
 /* var angle=p2.angle+Math.PI;
  var speed=p2.speed;
  if (angle<0) angle+=2*Math.PI;
  System.out.println(p2.angle);
  System.out.println(p1.dX);
  sub(p1,p2.angle, p2.speed);
  sub(p2,p2.angle, p2.speed);
  if (p1.speed==1 or p2.speed==1){
      line_for_foton (p1,p2,ang);
  } else  {
  line(p1,p2, ang);
   }
  sub(p1,angle, speed);
  sub(p2,angle, speed);  

   */
}

public class Particle{//класс частиц
    public attribute name:String ;
    public attribute speed:Number on replace {if (speed==0) angle=0};
    public attribute startspeed:Number;
    public attribute color:Color;
    public attribute x:Number ;
    public attribute y:Number ;
    public attribute dX=bind Math.cos(angle)*speed*10/sp ;    
    public attribute startx:Number on replace{ x = startx; };
    public attribute starty:Number on replace{ y = starty; };
    public attribute radius:Number ;
    public attribute mass:Number on replace {if (mass==0) speed=1};
    public attribute dY=bind Math.tanh(arsh(Math.sinh(arth(speed))*Math.sin(angle)))*10/sp;
    public attribute angle:Number;
    public attribute startangle:Number;
    public attribute flag=0;
    public attribute flag2=0;
    public function compare(p:Particle, eps:Number):Boolean{
        return (p.speed == 0.0 and speed == 0.0) or ( abs(p.angle - angle) < eps  and abs(p.speed - speed) < eps );
    }
    
    public function toString():String{
        var res = "Particle\{\n";
        res += "name: {name}\n";
        res += "angle: {angle}\n";
        res += "speed: {speed}\n";
        res += "}";
        return res;
    } 
     }
public class ParticleSystem extends CustomNode{//сама модель
    public attribute particles: Particle[];
          
    
    public attribute dt:Number = 0.05 ;
      public function run () { 
          for (particle in particles){
          sp = Math.max(sp,particle.speed);
          if (particle.flag2==0){
              particle.flag2++;
          particle.startangle=particle.angle;
          particle.startspeed=particle.speed;
          particle.startx=particle.x;
          particle.starty=particle.y;
          }
  }
  
      
        for (particle1 in particles){
       
            for (particle2 in particles[
            particle| particle != particle1]){
                var minDistance = particle1.radius + particle2.radius;
                if( Math.sqrt(q(particle1.x - particle2.x)+q(particle1.y - particle2.y)) < minDistance and particle1.flag==0 and particle2.flag==0){//столкновение
                    
                    particle1.flag=1;
                    particle2.flag=1;
                    System.out.println("Particle collision!"); 
                 
                    
                    start(particle1, particle2, disang);     
/*for(test in tests){
    test.particle1=particle1;
    test.particle2=particle2;
    start(test.particle1, test.particle2, test.angle);
    test.compare();}  */              
                                    }
              if ( Math.sqrt(q(particle1.x - particle2.x)+q(particle1.y - particle2.y)) > minDistance and ((particle1.flag==1) or (particle2.flag==1))){
                  particle1.flag=0;
                  particle2.flag=0;
              }
        }
      }  
        for (particle in particles){//движение
            if (particle.angle<0) {particle.angle+=2*Math.PI;}
            if (particle.angle>=2*Math.PI) {particle.angle-=2*Math.PI;}
            particle.x =  particle.dX * dt *r +  particle.x;
            particle.y =  particle.dY * dt *r +  particle.y;
            for (particle1 in particles){ //торможение
              for (particle2 in particles){
                
                if (Math.sqrt(particle1.x*particle1.x+particle1.y*particle1.y)>150 or (
                     Math.sqrt(particle2.x*particle2.x+particle2.y*particle2.y)>150)) 
         {
                    particle1.speed=0;
                    
                    particle2.speed=0;
                    
                }
                }
                }
            
        }
        
    } 
    
    function create():Node{
        
        return Group{
            content:bind [  for (particle in particles){ 
                Circle{
                    radius: particle.radius
                    fill:  bind particle.color
                    centerX: bind particle.x
                    centerY: bind particle.y
                    onMouseDragged: function( e: MouseEvent ){
                        particle.startx = e.getX();
                        particle.x=e.getX();
                        particle.starty = e.getY();
                        particle.y = e.getY();
                    }
                    }
                     }
            ]
        }
    }
}
public class SetupParticleSystem extends CustomNode{

    public attribute particles:Particle[];    
    attribute ang:String on replace {
        disang = java.lang.Double.parseDouble(ang);
    };
    attribute index:Integer on replace{
        mass   = "{particles[index].mass}";
        speed = "{particles[index].speed}";
        x = "{particles[index].x}";
        y = "{particles[index].y}";
        angle = "{particles[index].angle}";
    };
    
    attribute mass:String on replace{
        particles [index].mass = java.lang.Double.parseDouble(mass);
    };
    
    attribute speed:String on replace{
        particles [index].speed = java.lang.Double.parseDouble(speed);
        particles [index].startspeed = java.lang.Double.parseDouble(speed);
    };
    
    attribute x:String on replace{
        
        particles [index].x = java.lang.Double.parseDouble(x);
    };
    attribute angle:String on replace{
        particles [index].angle = java.lang.Double.parseDouble(angle);
        particles [index].startangle = java.lang.Double.parseDouble(angle);
    };
    attribute y:String on replace{
       
        particles [index].y = java.lang.Double.parseDouble(y);        
    };
    
    function create():Node{
        return ComponentView {
            component: BorderPanel{
                left: List{
                    items: bind 
                        for (particle in particles) 
                        ListItem{
                            text: particle.name
                        }
                    selectedIndex: bind index with inverse  
                }
                center: FlowPanel{ content: GridPanel{
                    columns: 3, rows: 6
                    content: [
                        Label{ text: "Mass"},   
                        TextField{ columns: 7, text: bind mass with inverse},
                        Label {},
                        
                        Label{ text: "Speed"}, 
                        TextField{ columns: 7, text: bind speed with inverse},
                        Label{ text: bind "{%+10.4f particles[index].speed}"},
                        
                        Label{ text: "Coordinate x"}, 
                        TextField{ columns: 7, text: bind x with inverse},
                        Label{ text: bind "{%+10.4f particles[index].x}"},
                        
                        Label{ text: "Coordinate y"}, 
                        TextField{ columns: 7, text: bind y with inverse},
                        Label{ text: bind "{%+10.4f particles[index].y}"},
                        
                        Label{ text: "Angle"},
                        TextField{ columns: 7, text: bind angle with inverse},
                        Label{ text: bind "{%+10.4f particles[index].angle}"},
                        
                        Label{ text: "DisAng"},
                        TextField{ columns: 7, text: bind ang with inverse},
                        Label {},
                
            ]

                } }
                
                right: FlowPanel{ content: GridPanel{
                        rows: 3
                      content:[ 
                          Button{
                        text: bind if(r==1) then "Pause" else "Run" 
                        action: function(){timeline.start();
                        r=1-r;}
                    },
                              Button{
                                  text: "Reset"
                                  action:function(){
                                      for (particle in particles){
                                      particle.x=particle.startx;
                                      particle.y=particle.starty;
                                      particle.speed=particle.startspeed;
                                      particle.angle=particle.startangle;
                                      particle.flag=0;
                                      particle.flag2=0;
                                      sp=0;
                                             
                                                           }}
                              },
                              Button{
                                  text: "Add"
                              action:function(){l++; insert Particle{
                                          name: "Particle "+l
                                          startx: (Math.random()-0.5)*200
                                          starty: (Math.random()-0.5)*200
                                          radius: 4
                                          mass: 10
                                          color: Color.rgb(Math.random()*255, Math.random()*255, Math.random()*255)
                                      } into particles;
                                   } }]
            }
        }}
    }
    }
    }

    
        
    

public class TestCase{

    public attribute eps:Number = 0.1;
    public attribute angle: Number;
    
    public attribute particle1: Particle;
    public attribute particle2: Particle;
    public attribute result1: Particle;
    public attribute result2: Particle;

    public function print(p: Particle, res:Particle){
        System.out.println("===========  Particles are not equal  =============");
        System.out.println("particle: {p}");
        System.out.println("result: {res}");
    }
    
    public function compare(){
        if(not result1.compare(particle1, eps)){
            print(particle1, result1);
        }
        if(not result2.compare(particle2, eps)){
            print(particle2, result2);
        }
        
    }
}
    

function angle(x1:Number, y1:Number, x2:Number, y2:Number):Number{
    
    var scalar = x1 * x2  + y1 * y2;
    var length1 = Math.sqrt(x1 * x1 + y1 * y1);
    var length2 = Math.sqrt(x2 * x2 + y2 * y2);
    return Math.acos( scalar / ( length1 * length2));
}

    
function speedpt(ang1:Number, ang2:Number, m1:Number, m2:Number):Number{
    var a = Math.sin(ang1)/Math.sin(ang1+ang2)*m1/m2;
    var b = Math.sin(ang2)/Math.sin(ang1+ang2);
    var c = q(q(b)-q(a))+1-(q(a)+q(b))*2;
    var p = (-q(q(b)-q(a))+1)*2;
    var z = q(q(a)-q(b))-3+(q(b)+q(a))*2;
    var x = (-p-Math.sqrt(q(p)-c*z*4))/(c*2);
    return Math.sqrt(1-1/q(x))
}

public class RotatedLine extends CustomNode{

    public attribute color:Color;
    public attribute name:String;

    public attribute reflect:Boolean;

    
    public attribute centerX:Number;
    public attribute centerY:Number;
    
    public attribute startX:Number;
    public attribute startY:Number;
    
    
     public function create(){
         Group{
             content:[
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX + startX  
                    endY: bind centerY + startY
                },
                Circle{
                    centerX: bind centerX + startX
                    centerY: bind centerY + startY
                    radius: 5
                    fill: bind color
                    onMouseDragged: function( e: MouseEvent ):Void {
                        startX = e.getX() - centerX;
                        startY = e.getY() - centerY;
                    }                    
                },Text{
                    x: bind centerX + startX + 5
                    y: bind centerY + startY - 5
                    fill: Color.RED
                    font: Font {  size: 16  style: FontStyle.PLAIN }
                    content: name
                },
                if (reflect)
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX - startX  
                    endY: bind centerY - startY
                    strokeDashArray:  [ 10.0 ]
                } else  [] 
                
             ]

         }
     }
    
}

public class ParticleImage extends CustomNode{
    
    public attribute url: String;
    
    public attribute color:Color = Color.CORAL;
    public attribute lineColor:Color = Color.CORAL;

    public attribute scale:Number = 1.0;
    
    public attribute angle1:Number = bind angle(-startX, -startY, endX1, endY1);
    public attribute angle2:Number = bind angle(-startX, -startY, endX2, endY2);
    public attribute speed:Number = bind speedpt(angle1,angle2, java.lang.Double.parseDouble(mass1), java.lang.Double.parseDouble(mass2));
    public attribute disangle:Number = bind tch1(v1,v1,v2);/*bind 2*Math.atan(Math.sqrt(Math.tan(angle1)/Math.tan(angle2)));*/
    public attribute mass1: String="1";
    public attribute mass2: String="1";
    
    attribute m1=bind java.lang.Double.parseDouble(mass1);
    attribute m2=bind java.lang.Double.parseDouble(mass2);
    attribute v2=bind Math.tanh (arsh(m1/m2*Math.sinh(arth(speed))*Math.sin(angle1)/Math.sin(angle1+angle2)));
    attribute v1=bind m1*Math.sinh(arth(speed))/(m2+m1*Math.cosh(arth(speed)));
       
       
    attribute centerX:Number = 50;
    attribute centerY:Number = 50;
    
    attribute startX:Number = -140;
    attribute startY:Number = 0;
    
    attribute endX1:Number = 90;
    attribute endY1:Number = -90;
    
    attribute endX2:Number = 90;
    attribute endY2:Number = 90;
    
    
    
    public function create():Node{
        return {Group{
            transform: Scale{ x: bind scale y: bind scale}
            onMouseWheelMoved: function( e: MouseEvent ){
                var s = -e.getWheelRotation();
                scale += s / 10;
            }
            
            content:[
                ComponentView {
        component: GridPanel{
           rows: 2 columns: 2
           content: [ Label{ text: "Mass1"},   
                        TextField{ columns: 7, text: bind mass1 with inverse},
                        
                        Label{ text: "Mass2"}, 
                        TextField{ columns: 7, text: bind mass2 with inverse},]
        }
    },
                ImageView {
                    image: bind Image {
                        url: url
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    opacity: 0.01
                    fill: Color.WHITE
                    onMouseDragged: function( e: MouseEvent ){
                        centerX = e.getX();
                        centerY = e.getY();
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    stroke: bind color
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 120
                    stroke: bind color
                },
                RotatedLine{
                    name: "0"
                    reflect: true
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind startX with inverse
                    startY: bind startY with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "1"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX1 with inverse
                    startY: bind endY1 with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "2"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX2 with inverse
                    startY: bind endY2 with inverse
                    color: lineColor
                },
                Text{
                    x: 10
                    y: -100
                    fill: lineColor
                    content: bind "Angle 1: {%10.5f angle1}"
                },
                Text{
                    x: 10
                    y: -70
                    fill: lineColor
                    content: bind "Angle 2: {%10.5f angle2}"
                },
                Text{
                    x:10
                    y:-40
                    fill: lineColor
                    content: bind "Speed: {%10.5f speed}"
                },
                Text{
                    x: 10
                    y: -10
                    fill: lineColor
                    content: bind "DispAngle: {%10.5f disangle}"
                },
               
                /*GridPanel{
                    columns: 2, rows: 2
                    content: [
                        Label{ text: "Mass1"},   
                        TextField{ columns: 7, text: bind mass1 with inverse},
                        
                        Label{ text: "Mass2"}, 
                        TextField{ columns: 7, text: bind mass2 with inverse},
                       ] 
                    }*/
                ]
            

        }
    }
  }   
}
    
public class ImageItem{
    attribute name:String;
    attribute path:String;
    
}
    
var images = [ImageItem{ name: "Emission"}, ImageItem{ name: "Protons" path: "protons.png"}];
    
var imageIndex = 1;
var imageGroup = Group{
    content:[
        ParticleImage{
                        translateY: 170
                        centerX: 200
                        centerY: 200
                        url: bind "{__DIR__}{images[imageIndex].path}"
                        color: Color.GREEN
                    },
                
    ]

};
var group = Group{
    content: [ 
        SetupParticleSystem{ particles:bind  particleSystem.particles with inverse},
        particleSystem
    ]
};
Frame{
    title: "Particle Collision"
    width: 800
    height: 600
    closeAction: function(){ 
        System.exit(0); 
    }
    stage: Stage{
        content: [
            Group{
            content: bind [ 
                ComponentView{
                    translateX: 460
                    translateY: 140
                    component: ComboBox {
                    items: for (image in images)
                        ComboBoxItem {

                            text:  image.name
                            selected: true
                        }
                        selectedIndex: bind imageIndex with inverse
                    }
                }, if(0 < imageIndex ) 
                    ParticleImage{
                       translateY: 170
                       centerX: 200
                       centerY: 200
                       url: bind "{__DIR__}{images[imageIndex].path}"
                       color: Color.GREEN
                   }
               else group,
               /*if (imageIndex==0)[
                SetupParticleSystem{ particles:bind  particleSystem.particles with inverse},
            particleSystem] else []*/
               ]
            },
            
        ]
    }
    visible: true

}


Тестовые примеры:


public class Particle{//класс частиц

    public attribute startspeed:Number on replace { speed = startspeed; };
    public attribute startangle:Number on replace { angle = startangle; };

}

var tests = [ 
    TestCase{
        // p1 -> p2
        // centre mass angle = 0
        // p1 speed: 0.5, angle =  0
        // p2 speed: 0, angle =  0
        
        
        angle: Math.PI // or 0.0 ?   
        particle1: Particle{
            name: "I - Proton 1"
            startx: -50
            mass: electronMass
            startangle: 0.0
            startspeed: 0.5
        }
        particle2: Particle{
            name: "I - Proton 2"
            startx: 50
            mass: electronMass
            startangle: 0.0
            startspeed: 0.0
        }
        result1: Particle{
            name: "I - Result Proton 1"
            angle: 0.0
            speed: 0.0
        }
        result2: Particle{
            name: "I - Result Proton 2"
            angle: 0.0
            speed: 0.5
        }
    },
    
    TestCase{
        // p1 <- p2
        // centre mass angle = 0
        // p1 speed: 0, angle =  0
        // p2 speed: 0.5, angle =  PI  
        
        angle: Math.PI // 0.0
        particle1: Particle{
            name: "II - Proton 1"
            startx: -50
            mass: electronMass
            startangle: 0.0
        }
        particle2: Particle{
            name: "II - Proton 2"
            startx: 50
            mass: electronMass
            startangle: Math.PI
            startspeed: 0.5
        }
        result1: Particle{
            name: "II - Result Proton 1"
            angle: 0.0
            speed: 0.5
        }
        result2: Particle{
            name: "II - Result Proton 2"
            angle: 0.0
            speed: 0.0
        }
    },
    TestCase{
        // p2
        // ^
        // |
        // p1
        
        // centre mass angle = 0
        // p1 speed: 0.5, angle =  PI / 2
        // p2 speed: 0, angle =  0

        angle: Math.PI // 0.0
        particle1: Particle{
            name: "III - Proton 1"
            startx: 0.0
            starty: -50.0
            mass: electronMass
            startangle: Math.PI / 2
            startspeed: 0.5
        }
        particle2: Particle{
            name: "III - Proton 2"
            startx: 50
            mass: electronMass
            startangle: 0.0
            startspeed: 0.0
        }
        result1: Particle{
            name: "III - Result Proton 1"
            angle: 0.0
            speed: 0.0
        }
        result2: Particle{
            name: "III - Result Proton 2"
            angle: Math.PI / 2
            speed: 0.5
        }
    },
    TestCase{
        // p1 -> <- p2
        
        // centre mass angle = 0
        // p1 speed: 0.5, angle =  0
        // p2 speed: 0.5, angle =  PI

        angle: Math.PI // 0.0
        particle1: Particle{
            name: "IV - Proton 1"
            startx: -50
            mass: electronMass
            startangle: 0
            startspeed: 0.5
        }
        particle2: Particle{
            name: "IV - Proton 2"
            startx: 50
            mass: electronMass
            startangle: Math.PI
            startspeed: 0.5
        }
        result1: Particle{
            name: "IV - Result Proton 1"
            angle: 0.0
            speed: 0.0
        }
        result2: Particle{
            name: "IV - Result Proton 2"
            angle: 0
            speed: 0
        }
    },    

];

for(test in tests){
    start(test.particle1, test.particle2, test.angle);
    test.compare();
}

Определение угла разлета частиц на фотоснимке[]

Фотоснимок сталкивающихся протонов:

AngleDetection 1


Увеличение фотоснимка и определение углов разлета частиц:

AngleDetection 2


Программа:

import javafx.application.Frame;
import javafx.application.Stage;

import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.geometry.*;
import javafx.scene.transform.*;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.input.MouseEvent;


import java.lang.Math;
import java.lang.System;

function q (w:Number):Number{//возведение в квадрат
    return w*w
}
function arch (x:Number):Number{//~косинуа
    return Math.log(x+Math.sqrt(q(x)-1)) 
}
function angle(x1:Number, y1:Number, x2:Number, y2:Number):Number{
    
    var scalar = x1 * x2  + y1 * y2;
    var length1 = Math.sqrt(x1 * x1 + y1 * y1);
    var length2 = Math.sqrt(x2 * x2 + y2 * y2);
    return Math.acos( scalar / ( length1 * length2));
}
function speedpt(ang1:Number, ang2:Number):Number{
    var a = Math.sin(ang1)/Math.sin(ang1+ang2);
    var b = Math.sin(ang2)/Math.sin(ang1+ang2);
    var c = q(q(b)-q(a))+1-(q(a)+q(b))*2;
    var p = (-q(q(b)-q(a))+1)*2;
    var z = q(q(a)-q(b))-3+(q(b)+q(a))*2;
    var x = (-p-Math.sqrt(q(p)-c*z*4))/(c*2);
    return Math.sqrt(1-1/q(x))
}

class RotatedLine extends CustomNode{

    public attribute color:Color;
    public attribute name:String;

    public attribute reflect:Boolean;

    
    public attribute centerX:Number;
    public attribute centerY:Number;
    
    public attribute startX:Number;
    public attribute startY:Number;
    
    
     public function create(){
         Group{
             content:[
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX + startX  
                    endY: bind centerY + startY
                },
                Circle{
                    centerX: bind centerX + startX
                    centerY: bind centerY + startY
                    radius: 5
                    fill: bind color
                    onMouseDragged: function( e: MouseEvent ):Void {
                        startX = e.getX() - centerX;
                        startY = e.getY() - centerY;
                    }                    
                },Text{
                    x: bind centerX + startX + 5
                    y: bind centerY + startY - 5
                    fill: Color.RED
                    font: Font {  size: 16  style: FontStyle.PLAIN }
                    content: name
                },
                if (reflect)
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX - startX  
                    endY: bind centerY - startY
                    strokeDashArray:  [ 10.0 ]
                } else  [] 
                
             ]

         }
     }
    
}

class ParticleImage extends CustomNode{
    
    public attribute url: String;
    
    public attribute color:Color = Color.CORAL;
    public attribute lineColor:Color = Color.CORAL;

    public attribute scale:Number = 1.0;
    
    public attribute angle1:Number = bind angle(-startX, -startY, endX1, endY1);
    public attribute angle2:Number = bind angle(-startX, -startY, endX2, endY2);
    public attribute speed:Number = bind speedpt(angle1,angle2);
    public attribute disangle:Number = bind 2*Math.atan(Math.sqrt(Math.tan(angle1)/Math.tan(angle2)));
    
    attribute centerX:Number = 50;
    attribute centerY:Number = 50;
    
    attribute startX:Number = -140;
    attribute startY:Number = 0;
    
    attribute endX1:Number = 90;
    attribute endY1:Number = -90;
    
    attribute endX2:Number = 90;
    attribute endY2:Number = 90;
    
    
    
    public function create(){
        Group{
            transform: Scale{ x: bind scale y: bind scale}
            onMouseWheelMoved: function( e: MouseEvent ){
                var s = -e.getWheelRotation();
                scale += s / 10;
            }
            
            content:[
                ImageView {
                    image: Image {
                        url: url
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    opacity: 0.01
                    fill: Color.WHITE
                    onMouseDragged: function( e: MouseEvent ){
                        centerX = e.getX();
                        centerY = e.getY();
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    stroke: bind color
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 120
                    stroke: bind color
                },
                RotatedLine{
                    name: "0"
                    reflect: true
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind startX with inverse
                    startY: bind startY with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "1"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX1 with inverse
                    startY: bind endY1 with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "2"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX2 with inverse
                    startY: bind endY2 with inverse
                    color: lineColor
                },
                Text{
                    x: 10
                    y: 50
                    fill: lineColor
                    content: bind "Angle 1: {%10.5f angle1}"
                },
                Text{
                    x: 10
                    y: 80
                    fill: lineColor
                    content: bind "Angle 2: {%10.5f angle2}"
                },
                Text{
                    x:10
                    y:110
                    fill: lineColor
                    content: bind "Speed: {%10.5f speed}"
                },
                Text{
                    x: 10
                    y: 140
                    fill: lineColor
                    content: bind "DispAngle: {%10.5f disangle}"
                }
            ]

        }
    }
    
}

Frame {
    title: "MyApplication"
    width: 600
    height: 500
    closeAction: function() { 
        java.lang.System.exit( 0 ); 
    }
    visible: true

    stage: Stage {
        content: [
            ParticleImage{
                centerX: 200
                centerY: 200
                url: "{__DIR__}protons.jpg"
                color: Color.GREEN
            }

        ]
    }
}


Определение угла разлета частиц на фотоснимке - Полная программа[]

import javafx.animation.*;
import javafx.application.*;
import javafx.ext.swing.*;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.geometry.*;
import javafx.scene.transform.*;
import java.lang.Math;
import java.lang.*;
import java.lang.System;
import javafx.input.*;




var r=0;
var nu=1;
var h=6.6256e-34;
var c=299792458;
var v = 0.11;//скорость налетающей частицы относительно скорости света
var sp=0.0 ;
var l=2;
var protonMass = 0.1;
var electronMass = 0.01;
var tests = [
    TestCase{
        angle: 0.0
        result1: Particle{
            name: "Result Proton 1"
            angle: 0.0
            speed: 0.0
        }
        result2: Particle{
            name: "Result Proton 2"
            angle: 0.0
            speed: 0.5
        }
    }
    
];
var timeline = Timeline {
        keyFrames:  KeyFrame { time: 0.01s,  action: function() { particleSystem.run() 
        } } 
    repeatCount: java.lang.Double.POSITIVE_INFINITY
} 
var particleSystem = ParticleSystem{
    transform: Transform.translate(400, 300)
            
    particles: [
        Particle{
            name: "Particle 1"
            mass: 10
            radius: 4
            startx: -146
            starty: 0            
            speed: 0.5
            color: Color.GREEN
            
        },
        Particle{
            name: "Particle 2"
            mass: 10
            radius: 4
            startx: 0
            starty: 0
            speed: 0
            color: Color.RED
            
        }
    ]
           
};

function abs (x:Number):Number {
    return if ( 0 < x ) then x else -x;
} 
function q (w:Number):Number{//возведение в квадрат
    return w*w
}
function arth (x:Number):Number{//обратная к гиперболическому тангенсу
    return if (x==1) 1
    else  Math.log((1+x)/(1-x))/2
}
function arch (x:Number):Number{//~косинуа
    return Math.log(x+Math.sqrt(q(x)-1)) 
}
function arsh (x:Number):Number{//~синуса
    return Math.log(x+Math.sqrt(q(x)+1))
}
function tch (ang:Number, v1:Number, v2:Number):Number{//по теореме косинусов находит сторону по сторонам и углу
   return if (ang==0) 0 
   else if (ang==Math.PI) (v1+v2)/(1+v1*v2)
      else   Math.tanh(arch((Math.cosh(arth(v1))*Math.cosh(arth(v2))-Math.sinh(arth(v1))*Math.sinh(arth(v2))*Math.cos(ang))));
 }
function tch1 (v1:Number, v2:Number, v3:Number):Number{//по теореме косинусов находим угол разлета
    return if (v3==0) 0
    else if (v2==0) 0
    else if (v1==0) 0
    else Math.acos((Math.cosh(arth(v1))*Math.cosh(arth(v2))-Math.cosh(arth(v3)))/Math.sinh(arth(v1))/Math.sinh(arth(v2)))
}
function sub (p1:Particle, angle:Number, speed:Number){
    var pspeed=p1.speed;
    System.out.println("pspeed="+pspeed); 
    p1.speed=tch(p1.angle-angle,p1.speed,speed);
    System.out.println(p1.name+".speed="+p1.speed); 
    System.out.println(p1.name+".angle="+p1.angle); 
    System.out.println("angle="+angle); 
    System.out.println("speed="+speed); 
    if (p1.angle>angle) p1.angle-=tch1(pspeed,p1.speed,speed)
        
    else p1.angle+=tch1(pspeed,p1.speed,speed)-2*Math.PI;
        
    for (p in [p1]){
        if (p.angle<0) {p.angle+=2*Math.PI;}
        if (p.angle>2*Math.PI) {p.angle-=2*Math.PI;}
    }
System.out.println(p1.name+".angle="+p1.angle); 
System.out.println(p1.name+".speed="+p1.speed); 
}
function line (p1:Particle, p2:Particle,ang:Number){
    var k=0;
   if (ang<Math.PI) {ang=2*Math.PI-ang;k++;}
    if (ang==0) {ang=Math.PI;}
    var dopang;
    if (p1.angle!=0) dopang=p1.angle
    else dopang=p2.angle;
   
    var v1:Number;
    var v2:Number;
     System.out.println("ang="+ang); 
    v=(p1.speed+p2.speed)/(1+p1.speed*p2.speed);
     System.out.println("v="+v); 
    v1=p2.mass*Math.sinh(arth(v))/(p1.mass+p2.mass*Math.cosh(arth(v)));
     System.out.println("v1="+v1); 
    v2=p1.mass*Math.sinh(arth(v))/(p2.mass+p1.mass*Math.cosh(arth(v)));
     System.out.println("v2="+v2); 
    p1.angle=-tch1(v2, tch(ang-Math.PI, v1,v2),v1)-dopang;//угол отлета первой частицы
     System.out.println("p1.angle="+p1.angle); 
    p2.angle=tch1(v2,tch(ang,v2,v2),v2)-dopang;//угол отлета второй частицы
     System.out.println("p2.angle="+p2.angle); 
    p1.speed=tch(Math.PI-ang,v1,v2);
     System.out.println("p1.speed="+p1.speed); 
    p2.speed=tch(ang,v2,v2);
     System.out.println("p2.speed="+p2.speed); 
    if (k!=0){
        p1.angle=-p1.angle;
        p2.angle=-p2.angle;
        k--;
    }
    for (p in [p1, p2]){
        if (p.angle<0) {p.angle+=2*Math.PI;}
        if (p.angle>2*Math.PI) {p.angle-=2*Math.PI;}
    }
}
function line_for_foton (p1:Particle, p2:Particle, ang:Number){
    if (p1.speed==1){
        if (not(p2.speed==1)) p1.angle=Math.random()*Math.PI*2
    }
else p2.angle=Math.random()*Math.PI*2    
}
function start (p1:Particle,p2:Particle, ang:Number){
  var angle=p2.angle+Math.PI;
  var speed=p2.speed;
  if (angle<0) angle+=2*Math.PI;
  System.out.println(p2.angle);
  System.out.println(p1.dX);
  sub(p1,p2.angle, p2.speed);
  sub(p2,p2.angle, p2.speed);
  if (p1.speed==1 or p2.speed==1){
      line_for_foton (p1,p2,ang);
  } else  {
  line(p1,p2, ang);
   }
  sub(p1,angle, speed);
  sub(p2,angle, speed);  

   
}

public class Particle{//класс частиц
    public attribute name:String ;
    public attribute speed:Number on replace {if (speed==0) angle=0};
    public attribute startspeed:Number;
    public attribute color:Color;
    public attribute x:Number ;
    public attribute y:Number ;
    public attribute dX=bind Math.cos(angle)*speed*10/sp ;    
    public attribute startx:Number on replace{ x = startx; };
    public attribute starty:Number on replace{ y = starty; };
    public attribute radius:Number ;
    public attribute mass:Number on replace {if (mass==0) speed=1};
    public attribute dY=bind Math.sin(angle)*speed*10/sp;
    public attribute angle:Number;
    public attribute startangle:Number;
    public attribute flag=0;
    public attribute flag2=0;
    public function compare(p:Particle, eps:Number):Boolean{
        return (p.speed == 0.0 and speed == 0.0) or ( abs(p.angle - angle) < eps  and abs(p.speed - speed) < eps );
    }
    
    public function toString():String{
        var res = "Particle\{\n";
        res += "name: {name}\n";
        res += "angle: {angle}\n";
        res += "speed: {speed}\n";
        res += "}";
        return res;
    } 
     }
class TestCase{

    public attribute eps:Number = 0.1;
    public attribute angle: Number;
    
    public attribute particle1: Particle;
    public attribute particle2: Particle;
    public attribute result1: Particle;
    public attribute result2: Particle;

    public function print(p: Particle, res:Particle){
        System.out.println("===========  Particles are not equal  =============");
        System.out.println("particle: {p}");
        System.out.println("result: {res}");
    }
    
    public function compare(){
        if(not result1.compare(particle1, eps)){
            print(particle1, result1);
        }
        if(not result2.compare(particle2, eps)){
            print(particle2, result2);
        }
        
    }
}
public class ParticleSystem extends CustomNode{//сама модель
    public attribute particles: Particle[];
          
    
    public attribute dt:Number = 0.05 ;
      public function run () { 
          for (particle in particles){
          sp = Math.max(sp,particle.speed);
          if (particle.flag2==0){
              particle.flag2++;
          particle.startangle=particle.angle;
          particle.startspeed=particle.speed;
          particle.startx=particle.x;
          particle.starty=particle.y;
          }
  }
  
      
        for (particle1 in particles){
       
            for (particle2 in particles[
            particle| particle != particle1]){
                var minDistance = particle1.radius + particle2.radius;
                if( Math.sqrt(q(particle1.x - particle2.x)+q(particle1.y - particle2.y)) < minDistance and particle1.flag==0 and particle2.flag==0){//столкновение
                    
                    particle1.flag=1;
                    particle2.flag=1;
                    System.out.println("Particle collision!"); 
                 
                    
                    start(particle1, particle2, Math.PI/2);     
/*for(test in tests){
    test.particle1=particle1;
    test.particle2=particle2;
    start(test.particle1, test.particle2, test.angle);
    test.compare();}  */              
                                    }
              if ( Math.sqrt(q(particle1.x - particle2.x)+q(particle1.y - particle2.y)) > minDistance and ((particle1.flag==1) or (particle2.flag==1))){
                  particle1.flag=0;
                  particle2.flag=0;
              }
        }
      }  
        for (particle in particles){//движение
            if (particle.angle<0) {particle.angle+=2*Math.PI;}
            if (particle.angle>=2*Math.PI) {particle.angle-=2*Math.PI;}
            particle.x =  particle.dX * dt *r +  particle.x;
            particle.y =  particle.dY * dt *r +  particle.y;
            for (particle1 in particles){ //торможение
              for (particle2 in particles){
                
                if (Math.sqrt(particle1.x*particle1.x+particle1.y*particle1.y)>150 or (
                     Math.sqrt(particle2.x*particle2.x+particle2.y*particle2.y)>150)) 
         {
                    particle1.speed=0;
                    
                    particle2.speed=0;
                    
                }
                }
                }
            
        }
        
    } 
    
    function create():Node{
        
        return Group{
            content:bind [  for (particle in particles){ 
                Circle{
                    radius: particle.radius
                    fill:  bind particle.color
                    centerX: bind particle.x
                    centerY: bind particle.y
                    onMouseDragged: function( e: MouseEvent ){
                        particle.startx = e.getX();
                        particle.x=e.getX();
                        particle.starty = e.getY();
                        particle.y = e.getY();
                    }
                    }
                     }
            ]
        }
    }
}
public class SetupParticleSystem extends CustomNode{

    public attribute particles:Particle[];    
 
    attribute index:Integer on replace{
        mass   = "{particles[index].mass}";
        speed = "{particles[index].speed}";
        x = "{particles[index].x}";
        y = "{particles[index].y}";
        angle = "{particles[index].angle}";
    };
    
    attribute mass:String on replace{
        particles [index].mass = java.lang.Double.parseDouble(mass);
    };
    
    attribute speed:String on replace{
        particles [index].speed = java.lang.Double.parseDouble(speed);
        particles [index].startspeed = java.lang.Double.parseDouble(speed);
    };
    
    attribute x:String on replace{
        
        particles [index].x = java.lang.Double.parseDouble(x);
    };
    attribute angle:String on replace{
        particles [index].angle = java.lang.Double.parseDouble(angle);
        particles [index].startangle = java.lang.Double.parseDouble(angle);
    };
    attribute y:String on replace{
       
        particles [index].y = java.lang.Double.parseDouble(y);        
    };
    
    function create():Node{
        return ComponentView {
            component: BorderPanel{
                left: List{
                    items: bind 
                        for (particle in particles) 
                        ListItem{
                            text: particle.name
                        }
                    selectedIndex: bind index with inverse  
                }
                center: FlowPanel{ content: GridPanel{
                    columns: 3, rows: 5
                    content: [
                        Label{ text: "Mass"},   
                        TextField{ columns: 7, text: bind mass with inverse},
                        Label {},
                        
                        Label{ text: "Speed"}, 
                        TextField{ columns: 7, text: bind speed with inverse},
                        Label{ text: bind "{%+10.4f particles[index].speed}"},
                        
                        Label{ text: "Coordinate x"}, 
                        TextField{ columns: 7, text: bind x with inverse},
                        Label{ text: bind "{%+10.4f particles[index].x}"},
                        
                        Label{ text: "Coordinate y"}, 
                        TextField{ columns: 7, text: bind y with inverse},
                        Label{ text: bind "{%+10.4f particles[index].y}"},
                        
                        Label{ text: "Angle"},
                        TextField{ columns: 7, text: bind angle with inverse},
                        Label{ text: bind "{%+10.4f particles[index].angle}"},
                
            ]

                } }
                
                right: FlowPanel{ content: GridPanel{
                        rows: 3
                      content:[ 
                          Button{
                        text: bind if(r==1) then "Pause" else "Run" 
                        action: function(){timeline.start();
                        r=1-r;}
                    },
                              Button{
                                  text: "Reset"
                                  action:function(){
                                      for (particle in particles){
                                      particle.x=particle.startx;
                                      particle.y=particle.starty;
                                      particle.speed=particle.startspeed;
                                      particle.angle=particle.startangle;
                                      particle.flag=0;
                                      particle.flag2=0;
                                             
                                                           }}
                              },
                              Button{
                                  text: "Add"
                              action:function(){l++; insert Particle{
                                          name: "Particle "+l
                                          startx: (Math.random()-0.5)*200
                                          starty: (Math.random()-0.5)*200
                                          radius: 4
                                          mass: 10
                                          color: Color.rgb(Math.random()*255, Math.random()*255, Math.random()*255)
                                      } into particles;
                                   } }]
            }
        }}
    }
    }
    }
    
    
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.geometry.*;
import javafx.scene.transform.*;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.input.MouseEvent;
    

function angle(x1:Number, y1:Number, x2:Number, y2:Number):Number{
    
    var scalar = x1 * x2  + y1 * y2;
    var length1 = Math.sqrt(x1 * x1 + y1 * y1);
    var length2 = Math.sqrt(x2 * x2 + y2 * y2);
    return Math.acos( scalar / ( length1 * length2));
}

    
function speedpt(ang1:Number, ang2:Number):Number{
    var a = Math.sin(ang1)/Math.sin(ang1+ang2);
    var b = Math.sin(ang2)/Math.sin(ang1+ang2);
    var c = q(q(b)-q(a))+1-(q(a)+q(b))*2;
    var p = (-q(q(b)-q(a))+1)*2;
    var z = q(q(a)-q(b))-3+(q(b)+q(a))*2;
    var x = (-p-Math.sqrt(q(p)-c*z*4))/(c*2);
    return Math.sqrt(1-1/q(x))
}

class RotatedLine extends CustomNode{

    public attribute color:Color;
    public attribute name:String;

    public attribute reflect:Boolean;

    
    public attribute centerX:Number;
    public attribute centerY:Number;
    
    public attribute startX:Number;
    public attribute startY:Number;
    
    
     public function create(){
         Group{
             content:[
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX + startX  
                    endY: bind centerY + startY
                },
                Circle{
                    centerX: bind centerX + startX
                    centerY: bind centerY + startY
                    radius: 5
                    fill: bind color
                    onMouseDragged: function( e: MouseEvent ):Void {
                        startX = e.getX() - centerX;
                        startY = e.getY() - centerY;
                    }                    
                },Text{
                    x: bind centerX + startX + 5
                    y: bind centerY + startY - 5
                    fill: Color.RED
                    font: Font {  size: 16  style: FontStyle.PLAIN }
                    content: name
                },
                if (reflect)
                Line{   
                    startX: bind centerX
                    startY: bind centerY
                    endX: bind centerX - startX  
                    endY: bind centerY - startY
                    strokeDashArray:  [ 10.0 ]
                } else  [] 
                
             ]

         }
     }
    
}

class ParticleImage extends CustomNode{
    
    public attribute url: String;
    
    public attribute color:Color = Color.CORAL;
    public attribute lineColor:Color = Color.CORAL;

    public attribute scale:Number = 1.0;
    
    public attribute angle1:Number = bind angle(-startX, -startY, endX1, endY1);
    public attribute angle2:Number = bind angle(-startX, -startY, endX2, endY2);
    public attribute speed:Number = bind speedpt(angle1,angle2);
    public attribute disangle:Number = bind 2*Math.atan(Math.sqrt(Math.tan(angle1)/Math.tan(angle2)));
    
    attribute centerX:Number = 50;
    attribute centerY:Number = 50;
    
    attribute startX:Number = -140;
    attribute startY:Number = 0;
    
    attribute endX1:Number = 90;
    attribute endY1:Number = -90;
    
    attribute endX2:Number = 90;
    attribute endY2:Number = 90;
    
    
    
    public function create(){
        Group{
            transform: Scale{ x: bind scale y: bind scale}
            onMouseWheelMoved: function( e: MouseEvent ){
                var s = -e.getWheelRotation();
                scale += s / 10;
            }
            
            content:[
                ImageView {
                    image: bind Image {
                        url: url
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    opacity: 0.01
                    fill: Color.WHITE
                    onMouseDragged: function( e: MouseEvent ){
                        centerX = e.getX();
                        centerY = e.getY();
                    }
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 20
                    stroke: bind color
                },
                Circle{
                    centerX: bind centerX
                    centerY: bind centerY
                    radius: 120
                    stroke: bind color
                },
                RotatedLine{
                    name: "0"
                    reflect: true
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind startX with inverse
                    startY: bind startY with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "1"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX1 with inverse
                    startY: bind endY1 with inverse
                    color: lineColor
                },
                RotatedLine{
                    name: "2"
                    centerX: bind centerX
                    centerY: bind centerY
                    startX: bind endX2 with inverse
                    startY: bind endY2 with inverse
                    color: lineColor
                },
                Text{
                    x: 10
                    y: 50
                    fill: lineColor
                    content: bind "Angle 1: {%10.5f angle1}"
                },
                Text{
                    x: 10
                    y: 80
                    fill: lineColor
                    content: bind "Angle 2: {%10.5f angle2}"
                },
                Text{
                    x:10
                    y:110
                    fill: lineColor
                    content: bind "Speed: {%10.5f speed}"
                },
                Text{
                    x: 10
                    y: 140
                    fill: lineColor
                    content: bind "DispAngle: {%10.5f disangle}"
                }
            ]

        }
    }
    
}
    
class ImageItem{
    attribute name:String;
    attribute path:String;
    
}
    
var images = [ImageItem{ name: "Empty"}, ImageItem{ name: "Protons" path: "protons.png"}];
    
var imageIndex = 1;

Frame{
    title: "Particle Collision"
    width: 800
    height: 600
    closeAction: function(){ 
        System.exit(0); 
    }
    stage: Stage{
        content: [
            Group{
            content: bind [ 
                ComponentView{
                    translateX: 460
                    translateY: 140
                    component: ComboBox {
                    items: for (image in images)
                        ComboBoxItem {

                            text:  image.name
                            selected: true
                        }
                        selectedIndex: bind imageIndex with inverse
                    }
                }, if(0 < imageIndex ) 
                ParticleImage{
                    translateY: 170
                    centerX: 200
                    centerY: 200
                    url: bind "{__DIR__}{images[imageIndex].path}"
                    color: Color.GREEN
                }
               else []
               ]
            },
            SetupParticleSystem{ particles:bind  particleSystem.particles with inverse},
            particleSystem
        ]
    }
    visible: true

}

Тестирование функции столкновения частиц[]

Закон сохранения 4х импульса сталкивающихся частиц[]

 
 

Particles four impulse

 
 
 


1. , угол разлета в системе центра масс равен

  
 
 

Выразим из этих 2х уравнений угол через скорость :

 

Составим табличку для некоторых скоростей:

0.3 0.5 0.7
0.779 0.766 0.7398

Расчет скорости налетающей частицы[]

Particles four impulse

 
 
 


Дано: углы разлета частиц в лабораторной системе отсчета.

Надо найти скорость налетающей частицы.


 (I) 
 (II) 
 (III) 

Вспомогательные формулы:

 =>   
 => 


 (II) => 
 (I) =>  => 
     

тогда:

     =>

аналогично:

 


Обозначим:


тогда:

 
 

(III) =>

 

Рассмотрим выражение:

 

Возводим его в квадрат:

 

Еще раз возводим в квадрат:

 

Тогда

 
 


Теперь подставляем k, n и m:

 

Делим на :

 

В итоге получаем квадратное уровнение отностительно x. Собираем коэффициенты при , и , решаем квадратное уровнение и находим x.

Тогда:

 

Расчет скорости налетающей частицы (Версия 2)[]

Напишем закон сохранения взяв проекции на оси:


Т.к. массы частиц одинаковы, получаем из второго равенства, что углы отлета частиц равны, тогда:

Пользуясь формулой , выражаем квадрат синуса через косинус во втором равенстве, подставляем первое:


Решаем как квадратичное уравнение относительно переменной , и получаем:


А затем по формуле , находим значение скорости.

Фотоснимки столкновения частиц[]

Столкновение протонов 1

Protons1


Столкновение протонов 2

Protons2


Ссылки[]

Столкновение протонов
Теги для математических формул
Advertisement