If you're seeing this message, it means we're having trouble loading external resources on our website.

Jeżeli jesteś za filtrem sieci web, prosimy, upewnij się, że domeny *.kastatic.org i *.kasandbox.org są odblokowane.

Główna zawartość

Przyciąganie grawitacyjne

Prawdopodobnie najbardziej znaną siłą jest siła grawitacji. Na Ziemi o grawitacji myślimy głównie w kontekście jabłka spadającego sir Isaacowi Newtonowi na głowę. Grawitacja oznacza, że rzeczy spadają. Ale to jest tylko nasz sposób odczuwania grawitacji. Rzeczywistość jest trochę inna — zarówno jabłko, jak i Ziemia przyciągają się wzajemnie. Masa Ziemi jest tak ogromna, że przeważa ona w każdym oddziaływaniu grawitacyjnym między obiektami na powierzchni planety. Każdy obiekt mający masę działa siłą przyciągania grawitacyjnego na każdy inny obiekt obdarzony masą. Na ilustracji poniżej zobrazowano wzór wyliczający siłę tych oddziaływań:
Diagram sił oddziaływań grawitacyjnych między dwiema sferami
Przyjrzyjmy się temu wzorowi nieco bliżej.
  • Na tym rysunku F oznacza siłę przyciągania grawitacyjnego, wektor, który chcemy obliczyć i przkazać go do funkcji applyForce().
  • G oznacza tak zwaną stałą grawitacji, uniwersalną stałą fizyczną równą 6.67428 x 10^-11 metra do trzeciej przez kilogram i sekundę do kwadratu. Dla Isaaca Newtona i Alberta Einsteina wartość stałej grawitacji była bardzo ważna. Dla Ciebie, programisty w ProcessingJS jej liczbowa wartość nie ma wielkiego znaczenia. Potraktujmy ją po prostu jako stałą, dzięki której możemy sprawić, że siły grawitacji w modelowanym przez nas świecie są silniejsze lub słabsze. Przyjęcie, że stała ta wynosi po prostu 1, też wcale nie jest złym pomysłem.
  • m, start subscript, 1, end subscript i m, start subscript, 2, end subscript oznaczają masy obiektów 1 i 2. Jak już dyskutowaliśmy przy okazji drugiej zasady dynamiki (F, with, vector, on top, equals, M, A, with, vector, on top), masa jest także czymś, co możemy zignorować. W końcu obiekty, które rysujemy na ekranie, nie mają żadnej „fizycznej” masy. Z drugiej strony, jeśli uwzględnimy wartości mas, możemy przeprowadzić ciekawszą symulację, w której siła przyciągania grawitacyjnego pochodząca od„większego” obiektu będzie silniejsza niż siła grawitacji pochodząca od mniejszego obiektu.
  • r, with, hat, on top oznacza wektor jednostkowy skierowany od masy 1 do masy 2. Jak za chwilę zobaczymy, możemy obliczyć ten wektor, odejmując od siebie wektory położenia jednej masy od drugiej .
  • r, squared oznacza kwadrat odległości pomiędzy masami. Zastanówmy się przez chwile nad tym. Wszystko, co znajduje się w liczniku tego wzoru —G, m, start subscript, 1, end subscript, m, start subscript, 2, end subscript—ma tę własność, że im jest większe, tym większa jest siła oddziaływania grawitacyjnego. Im większa masa, tym większa siła grawitacji. Im większa stała grawitacji G, tym większa siła. Kiedy dzielimy przez coś, sytuacja jest odwrotna. Wielkość siły jest odwrotnie proporcjonalna do kwadratu odległości pomiędzy masami. Im dalej od siebie znajdują się obdarzone masą obiekty, tym słabsza jest siła grawitacji pomiędzy nimi; im są bliżej, tym jest silniejsza.
Teraz wzór ten powinien stać się bardziej zrozumiały. Przeanalizowaliśmy zarówno ilustrację jak i poszczególne komponenty wzoru. Teraz czas na przetłumaczenie wzorów matematycznych na kod ProcessingJS. Przyjmijmy następujące założenia.
Mamy dwa obiekty i wiemy że:
  1. Każdy obiekt ma lokalizację PVector: location1 i location2.
  2. Każdy obiekt ma masę wyrażoną numerycznie: mass1 i mass2.
  3. Znana jest stała grawitacyjna G.
Biorąc pod uwagę te założenia chcemy wyliczyć wartość PVector force, siłę odziaływania grawitacyjnego. Rozłożymy to na dwie części. Najpierw policzymy kierunek siły r, with, hat, on top w powyższym wzorze. Potem policzymy wartość siły zgodnie z masą i odległością.
Pamiętasz w jaki sposób spowodowaliśmy że obiekt przyspieszał w kierunku kursora? Zrobimy tu dokładnie to samo.
Wektor jest różnicą między dwoma punktami. By wektor wskazywał kierunek od koła do kursora myszy po prostu odejmowaliśmy jeden punkt od drugiego:
var dir = PVector.sub(mouse, location);
W naszym przypadku kierunek siły przyciągającej obiekt 1 do obiektu 2 jest równy:
var dir = PVector.sub(location1, location2);
Nie zapominajmy że potrzebny jest nam wektor jednostkowy, wektor który mówi nam wyłącznie o kierunku, musimy więc znormalizować wektor który powstał po odjęciu położeń:
dir.normalize();
Teraz mamy już kierunek działania siły. Musimy więc policzyć moduł i odpowiednio zeskalować wektor.
var m = (G * mass1 * mass2) / (distance * distance);
dir.mult(m);
Jedynym problemem jest to że nie znamy odległości. G, mass1 imass2 zostały nam podane, odległość jednak musi zostać policzona najpierw, inaczej powyższy kod nie będzie działał. Należy się zastanowić czy nie dysponujemy już wektorem wskazującym z jednego położenia do drugiego? Czy długość tego wektora nie byłaby odległością między tymi dwoma obiektami?
Dodając jedną linię kodu obliczającą moduł wektora przed jego normalizacją otrzymamy odległość.
// Wektor wskazujący z jednego obiektu na drugi
var force = PVector.sub(location1, location2);

// Długość wektora (moduł) tego wektora jest odległością pomiędzy obiektami.
var distance = force.magnitude();

// Użyj wzoru na siłę oddziaływania grawitacyjnego by wyliczyć wartość siły.
var strength = (G * mass1 * mass2) / (distance * distance);

// Znormalizuj i zeskaluj wektor do odpowiedniego modułu.
force.normalize();
force.mult(strength);
Zauważ że zmieniłem nazwę PVector “dir” na “force”. Gdy już zakończymy wszystkie obliczenia PVector który mieliśmy na początku przyjmie wartość siły oddziaływania którą chcieliśmy wyliczyć od początku.
Teraz gdy już opracowaliśmy kwestię obliczania siły przyciągania (naśladowania grawitacji) w kodzie skupimy się na zastosowaniu tego w kodzie rzeczywistego programu ProcessingJS. Wcześniej w tym rozdziale utworzyliśmy prosty obiekt typu Mover—obiekt z położeniem, prędkością i przyspieszeniem określonymi typem PVector oraz metodą applyForce(). Weźmy tą klasę i umieśćmy ją w programie zawierającym:
  • Pojedynczy obiekt typu Mover.
  • Pojedynczy obiekt typu Attractor (nowy typ obiektu który będzie miał stałe położenie).
Obiekt Mover będzie przyciągany przez obiekt Attractor jak poniższym diagramie.
Prace możemy zacząć poprzez stworzenie prostej wersji obiektu Attractor—nadając mu położenie i masę oraz metodę wyświetlającą go na ekranie (wiążącą masę obiektu z jego wielkością na ekranie).
var Attractor = function() {
    this.position = new PVector(width/2, height/2);
    this.mass = 20;
    this.G = 1;
    this.dragOffset = new PVector(0, 0);
    this.dragging = false;
    this.rollover = false;
};

// Metoda wyświetlająca Attractor
Attractor.prototype.display = function() {
    ellipseMode(CENTER);
    strokeWeight(4);
    stroke(0);
    fill(175, 175, 175, 200);
    ellipse(this.position.x, this.position.y, this.mass*2, this.mass*2);
};
Po zdefiniowaniu tego możemy utworzyć instancję obiektu typu Attractor.
var mover = new Mover();
var attractor = new Attractor();

draw = function() {
    background(50, 50, 50);

    attractor.display();
    mover.update();
    mover.display();
};
Idzie nam dość dobrze: mamy program złożony z obiektów typu Mover i Attractor. Ostatnim elementem układanki jest jak spowodować przyciąganie jednego obiektu do drugiego. Jak spowodować żeby oba obiekty “rozmawiały” ze sobą?
Istnieje wiele rozwiązań architektonicznych tego problemu. Poniżej jest wypisane kilka z nich.
Zadaniefunkcja
1. Funkcja pobierająca obiekty typu Attractor i Mover:attraction(a, m);
2. Metoda w obiekcie Attractor przyjmująca argument typu Mover:a.attract(m);
3. Metoda w obiekcie Mover przyjmująca argument typu Attractor:mover.attractedTo(a);
4. Metoda w obiekcie Attractor przyjmująca argument typu Mover zwracająca obiekt typu PVector który jest siłą przyciągania. Ta siła przyciągania zostaje przekazana do metody applyForce() obiektu Mover.var f = a.calculateAttraction(m); mover.applyForce(f);
Warto spojrzeć na szereg opcji umożliwiających utworzenie systemu komunikacji między obiektami, można też argumentować za i przeciw każdemu z nich. Zacznijmy od odrzucenia pierwszej z nich ponieważ podejcie zorientowane obiektowo jest dużo lepszym wyborem niż funkcja nie związana ani z obiektem Mover ani obiektem Attractor. Jedyna różnica między opcjami 2 i 3 polega na różnicy między zdaniami “Obiekt Attractor przyciąga obiekt Mover” a “Obiekt Mover jest przyciągany przez obiekt Attractor”. Opcja 4 wydaje się być najbardziej odpowiednia w kontekście tego kursu. W końcu spędziliśmy dużo czasu nad metodą applyForce() i wydaje mi się że nasze przykłady będą bardziej zrozumiałe jeżeli będziemy się trzymać tej konwencji.
Innymi słowy, w miejscu gdzie mieliśmy:
var f = new PVector(0.1, 0); // Przykładowa siła
mover.applyForce(f);
Teraz mamy:
var f = a.calculateAttraction(m); // Siła oddziaływania grawitacyjnego między dwoma obiektami
mover.applyForce(f);
Więc naszą funkcję draw() możemy napisać w następujący sposób:
draw = function() {
    background(50, 50, 50);

    // Calculate attraction force and apply it
    var f = a.calculateAttraction(m);
    mover.applyForce(f);

    attractor.display();
    mover.update();
    mover.display();
};
Już prawie skończyliśmy. Ponieważ zdecydowaliśmy się umieścić metodę calculateAttraction() w obiekcie typu Attractor będziemy musieli napisać tą funkcję. Funkcja ta będzie przyjmować obiekt typu Mover i zwracać PVector. A co w tej funkcji? Wszystkie obliczenia matematyczne które opracowaliśmy wcześniej licząc przyciąganie grawitacyjne!
Attractor.prototype.calculateAttraction = function(mover) {

    // What's the force's direction?
    var force = PVector.sub(this.position, mover.position);    
    var distance = force.mag();
    force.normalize();

    // Jaka jest wartość tej siły?
    var strength = (this.G * this.mass * mover.mass) / (distance * distance);
    force.mult(strength);

    //  Zwróć wartość siły w celu jej zastosowania w dalszych obliczeniach!
    return force;
};
Gotowe. Prawie. W zasadzie. Jest jeden mały szkopuł który musimy dopracować. Spójrzmy na powyższy kod. Widzisz ten symbol dzielenia? Jeżeli kiedykolwiek mamy do czynienia z takim symbolem musimy zadać sobie pytanie: co stanie się jeżeli odległość będzie bardzo, bardzo małą liczbą albo (co gorsza) zerem!? Dobrze wiemy że nie da się dzielić przez zero a jeżeli podzielimy przez liczbę taką jak 0,0001 to będzie to równoznaczne z mnożeniem przez 10 000! Tak, to jest rzeczywisty wzór na siłę grawitacji, ale nie jesteśmy w prawdziwym świecie. Obecnie znajdujemy się w świecie ProcessingJS. A w tym świecie Mover może znaleźć się bardzo, bardzo blisko Attractora i siła oddziaływania grawitacyjnego byłaby tak wielka że Mover po prostu wyleciałby poza ekran. Biorąc to pod uwagę, praktycznym byłoby precyzyjne określenie zakresu wartości jakie może przyjmować odległość. Może bez względu na faktyczne położenie obiektu Mover powinniśmy przyjąć że nigdy nie jest bliżej niż 5 pikseli i nigdy więcej niż 25 pikseli od obiektu Attractor.
distance = constrain(distance, 5, 25);
Z tego samego powodu dla którego ograniczyliśmy minimalną odległość, musimy ograniczyć maksymalną odległość. Załóżmy że obiekt Mover jest położony 500 pikseli od obiektu Attractor (przypuszczenie dosyć rozsądne), wtedy będziemy dzielić siłę przez 250 000. Wartość ta będzie wtedy tak mała że równie dobrze moglibyśmy nie brać jej po uwagę.
Teraz to Ty musisz zadecydować które zachowanie Ci odpowiada. Ale w przypadku "chcę mieć rozsądnie wyglądające przyciąganie które nie będzie absurdalnie słabe albo silne" ograniczenie odległości jest dobrym rozwiązaniem.
Połączmy to teraz w jeden program. Obiekt typu Mover nie zmienił się wcale ale teraz nasz program posiada obiekt Attractor i kod który wiąże je ze sobą. Dodatkowo dodaliśmy kod do służący do kontrolowania obiektu Attractor myszką co pozwala na łatwiejsze obserwowanie efektów naszych działań.
Moglibyśmy oczywiście rozszerzyć ten przykład o tablicę zawierającą wiele obiektów typu Mover tak jak w przypadku zrobiliśmy w przypadku tarcia i oporu. Główną zmianą którą wprowadziliśmy jest dostosowanie typu Mover tak żeby przyjmował masę x i y (tak samo jak wcześniej) oraz inicjalizował tablicę losowo rozmieszczonych obiektów Mover i dla wszystkich obiektów w tej tablicy wyliczał ich siłę przyciągania:
var movers = [];
var attractor = new Attractor();

for (var i = 0; i < 10; i++) {
    movers[i] = new Mover(random(0.1, 2), random(width), random(height));
}

draw = function() {
    background(50, 50, 50);

    attractor.display();
    for (var i = 0; i < movers.length; i++) {
        var force = attractor.calculateAttraction(movers[i]);
        movers[i].applyForce(force);

        movers[i].update();
        movers[i].display();
    }
};

Ten kurs "Symulacje Natury" jest pochodną z "Natury Kodu " stworzonej przez Daniela Shiffmana, użytej pod licencją Creative Commons Attribution-NonCommercial 3.0 Unported License.