Pokazywanie postów oznaczonych etykietą VPython. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą VPython. Pokaż wszystkie posty

1 lipca 2018

Jupyter VPython 7

Jakoś tak się złożyło, że kiedy ja zabrałem się za bibliotekę VPython w 2016 roku, projekt był właśnie porzucony, a developerzy skupili się na wersji opartej o Jupytera i GlowScript-a. GlowScript-a umożliwia, kompilację kodu python-a do JavaScipt-u. Powstał nawet bardzo fajny serwis trinket.io, który zamyka cały proces tworzenia w przeglądarce. Jako narzędzie edukacyjne, używanie VPythona stało się jeszcze wygodniejsze. Wraz z wersją Ubuntu 18.4, paczka VPythona została usunięta z repozytorium.
Trochę szukania i okazuje się, że nie jest tak źle. Strona VPython informuje o projekcie IVisual, jednak stackoverflow sugerował wersję VPython. Nie wiem jak jest między nimi różnica, zacząłem od tego drugiego i przy nim pozostałem.
Instalacja poszła z pewnymi problemami. Zanim zostanie zainstalowany VPython, trzeba zainstalować paczkę Jupyter (w tej kolejności), jest to spowodowane jakimś bugiem:
mkdir vpython3
cd vpython3/
virtualenv -p python3 venv
source venv/bin/activate
pip3 install jupyter
pip3 install vpython
W API pojawiły się pewne drobne zmiany, dlatego postanowiłem przepisać standardowy przykład "bouncing ball" na nową wersję. Przy okazji odkryłem na stronie projektu informacje o module povexport do przechwytywania obrazu animacji. Wcześniej przechwytywałem obraz z ekranu (za pomocą PIL) i wycinałem obszar w którym pojawia się okno animacji.
Przykład:
import vpython as vp
import povexport

scene = vp.canvas(title='3D scene', 
                  x=0, y=0, width=400, height=400, 
                  center=vp.vector(0, 0, -0), autoscale=False)
vp.display(scene)

starting_height = 4
floor = vp.box(pos=vp.vector(0, 0, 0), length=4, height=0.5, width=4, color=vp.color.green)
ball = vp.sphere(pos=vp.vector(0, starting_height, 0), radius=1, color=vp.color.red)
ball.velocity = vp.vector(0, -1, 0)
dt = 0.01

frame = 0
while 1:
    vp.rate(100)
    ball.pos = ball.pos + ball.velocity*dt
    if ball.pos.y < ball.radius:
        ball.velocity.y = abs(ball.velocity.y)
    else:
        ball.velocity.y = ball.velocity.y - 9.8*dt
    
    file_name = 'img-%04d.pov' % frame
    inclist = ['colors.inc', 'stones.inc', 'woods.inc', 'metals.inc']
    povexport.export(scene, filename=file_name, include_list=inclist)
    frame += 1
        
    if ball.pos.y > starting_height:
        break
Co się zmieniło (dla mnie):
  • nowy moduł importu (zamiast visual jest vpython)
  • parametry przyjmują explicit obiekt vector i nie tolerują trójelementowej krotki
  • funkcji display zmieniła nazwę na canvas
Słów kilka o povexport. Sprytna biblioteka, która zapisuje informacje o bryłach i powierzchni w tekstowym języku/formacie SDL (Scene Description Language). Dane te następnie można przetworzyć za pomocą programu jak povray na format graficzny np. png i dalej skonstruować animację. Daje to całkiem ciekawe możliwości, bowiem VPython w założeniu oferuje tylko proste możliwości graficzne, povray daje dużo więcej możliwości jeżeli chodzi o renderowanie i ray tracing.
Nie obyło się bez małej ingerencji w kod, gdyż domyślnie obliczania ustawień odległości kamery w povexport były zbyt małe. Może coś robiłem nie tak, ostatecznie zmieniłem jedną linijkę.
# ...
# cpos = 1.5*displayscale*canv.camera.pos # 1.5 is a not understood fudge factor
  cpos = 10*displayscale*canv.camera.pos
# ...
W zasadzie, można pokusić się również o stworzenie skryptu, który edytuje pliki .pov i zmienia linijki z ustawieniami kamery. Zmiana w pliku .pov, którą uzyskałem:
camera {
    right <-image_width/image_height, 0, 0>      // vpython uses right-handed coord. system
    location <0.000000, 0.000000, 173.205081>
    up <0.000000, 1.000000, 0.000000>
    look_at <0.000000, 0.000000, 0.000000>
    angle 60.000000
}
Przerobienie plików z formatu .pov na .png:
for i in *.pov; do povray Height=400 Width=400 Display=false Output_File_Type=N $i 2>/dev/null; done
Nowa wersja skryptu ([1], [2]) do tworzenia animacji video/gif. Wyeliminowałem straty, które pojawiały się podczas składaniu video z plików .png, przez zastosowania kodeka H.264 i manipulacji "Constant Rate Factor" (uwaga, z ustawieniami CRF trzeba ostrożnie)
#!/bin/sh

PALETTE_FILE="tmp_pallete.png"
VIDEO_FILE="output_video.mp4"
INPUT_FILES="img-%4d.png"
OUTPUT_FILE="output.gif"
FILTERS="fps=25"

ffmpeg -r 100 -i $INPUT_FILES -c:v libx264 -crf 0 -preset veryslow $VIDEO_FILE
ffmpeg -v warning -i $VIDEO_FILE -vf "$FILTERS,palettegen" -y $PALETTE_FILE
ffmpeg -v warning -i $VIDEO_FILE -i $PALETTE_FILE -lavfi "$FILTERS [x]; [x][1:v] paletteuse" -y $OUTPUT_FILE
Efekt końcowy:

17 kwietnia 2017

[VPython] Kolizja dwóch ciał z obrotem

Trochę więcej kodu, bo sama detekcja kolizji jest bardziej skomplikowana (szczególnie dla większej ilości krawędzi).

18 listopada 2016

[VPython] Siła sprężystości

Dwie symulacje, właściwie bliźniacze, w których prezentowane jest działanie siły sprężystości (bez tłumienia). Każdy układ ma inną stałą sprężystości i położenie początkowe, dlatego zachowują się inaczej. W pierwszym przykładzie na układ działa dodatkowo siła grawitacji


W drugim przykładzie mamy ciało poruszające się na sprężynie horyzontalnie bez poślizgu. Nie jest rozważana siła grawitacja, która i tak by była równoważona przez oddziaływanie podłoża.

11 lipca 2016

Gif-y w wysokiej jakości

Znakomity wpis na temat uzysykiwania gif-ów wysokiej jakości:
Moja stara wersja korzystała ze zbyt wielu kroków. W dodatku nie mogłem zmusić programu convert do ustawienia mniejszego opóźnienia pomiędzy ramkami niż 10. W przykładzie poniżej zrezygnowałem ze skalowania.
ffmpeg -i video.avi -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif
Lepsza wersja (także bez skalowania). Kroków jest na tyle dużo, że zamknąłem wszystko w jednym skrypcie, ale convert nie jest już wykorzystywany, ponieważ sam ffmpeg dobrze sobie radzi z tworzeniem plików tego formatu:
#!/bin/sh

palette="palette.png"
video="video.avi"

# filters="fps=15,scale=320:-1:flags=lanczos"
filters="fps=25"

mogrify -crop 384x362+8+30 +repage $(ls -v *.png)
ffmpeg -r 100 -f image2 -i img-%3d.png $video
ffmpeg -v warning -i $video -vf "$filters,palettegen" -y $palette
ffmpeg -v warning -i $video -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y output.gif
Obraz jest znacznie płynniejszy.

26 czerwca 2016

Rachunek wektorowy i książki

Kilka przydatnych książek, które znalazłem na wikibooks, ucząc się fizyki. Niektórym brakuje rozdziałów, ale i tak jestem pod wrażeniem. Warto się temu przyjrzeć, jako coś dodatkowego do klasycznych podręczników:
Coś z rachunku wektorowego - wirujący wektor, do którego stworzenia potrzebna jest macierz obrotu [1], [2].
Program koduj macierze przekształceń właściwą dla dwóch wymiarów:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from visual import *
import ImageGrab

display(x=0, y=0, width=400, height=400, userzoom=False,
        center=(0, 0, 1), foreground=(0, 0, 0), background=(1, 1, 1))

ar = arrow(pos=(0, 0, 0), axis=(10, 0, 0), shaftwidth=0.3)

total_angle = 0
angle = 0.1 / (2 * math.pi)
frame = 0
while total_angle <= 2 * math.pi:
    rate(100)

    nx = ar.axis.x * math.cos(angle) - ar.axis.y * math.sin(angle)
    ny = ar.axis.x * math.sin(angle) + ar.axis.y * math.cos(angle)
    nz = 0
    ar.axis = (nx, ny, nz)

    file_name = 'img-' + '{fr:03d}'.format(fr=frame) + '.png'
    frame += 1

    im = ImageGrab.grab((0, 0, 400, 400))
    im.save(file_name)

    total_angle += angle
    print total_angle

exit()

28 marca 2016

vpython, gify i mechanika klasyczna

Od dawna planuje zabrać się za naukę mechaniki klasycznej. To kolejne podejście, tym razem jednak, zacząłem od poszukiwania narzędzi, które pomogą mi w nauce. Zaczęło się od bardzo fajnego artykułu na temat biblioteki vpython, linki poniżej i inne przydatne:
Za cel eksperymentu postawiłem sobie, wygenerowanie gif-a, z animacją ruchu. Liczyłem, że wystarczy do tego ImageMagic, jednak tworzenie gif-ów jest na tyle zagmatwane, że trzeba było skorzystać również z pakietu FFmpeg. Efekt nie jest do końca satysfakcjonujący. Od pewnego czasu, za bardzo skupiam się nad idealną wersją prototypu. Co więcej staram się znaleźć odpowiedzi na wszystkie pytania jakie się pojawią podczas jego wytwarzania, szczególnie te poboczne. Nie taka był idea tego bloga, czas więc na refleksję.

W każdym razie, oto przerobiony przykład "bouncing ball" z niewielkimi modyfikacjami. Po pierwsze, skorzystałem z biblioteki PIL (Python Image Library), aby tworzyć zrzuty ekranu kolejnych klatek animacji. Biblioteka działa jedynie pod systemem Windows, a jej zamiennik pyscreenshot nie zdał rezultatu. Jakoś się z tym pogodziłem. Druga sprawa, to warunek przerwania animacji, czyli moment gdy piłka znów znajdzie się w tej samej pozycji.
from visual import *
import ImageGrab    # from PIL


starting_height = 4
floor = box (pos=(0, 0, 0), length=4, height=0.5, width=4, color=color.blue)
ball = sphere (pos=(0, starting_height, 0), radius=1, color=color.red)
ball.velocity = vector(0, -1, 0)
dt = 0.01

frame = 0
while 1:
    rate (100)
    ball.pos = ball.pos + ball.velocity*dt
    if ball.y < ball.radius:
        ball.velocity.y = abs(ball.velocity.y)
    else:
        ball.velocity.y = ball.velocity.y - 9.8*dt

    file_name = 'img-' + '{fr:03d}'.format(fr=frame)  + '.png'
    frame += 1

    im = ImageGrab.grab((0, 0, 500, 500))  # screen box from (0,0)-(500,500)
    im.save(file_name)                     # save image to disk

    if ball.pos.y > starting_height:
        exit()
Skrypt wygenerował około 140 zrzutów ekranu, które trzeba było poddać obróbce, nieoceniony okazał się tutaj ImageMagic. Kluczowym problem jest wysterowanie czasu pomiędzy kolejnymi klatkami.
Wszystkie obrazki zawierały ramki Windows-owego okienka, które trzeba było usunąć:
Przycinanie zdjęcia za pomocą mogrify (z ImageMagic). Pozycje i rozmiar dobrałem eksperymentalnie.
mogrify -crop 413x411+9+30 +repage $(ls -v *.png)
Chociaż ImageMagic potrafi generować gif-y, jednak za żadne skarby nie mogłem skonfigurować właściwego opóźnienia, pomiędzy kolejnymi klatkami. Szperając po internecie udało się ten problem ominąć, wymaga to jednak kroków pośrednich, w postaci utworzenia filmiku z animacją, a następnie przepuszczenia go jeszcze raz przez ffmpeg i convert (ImageMagic). Magiczny zestaw komend:
# Filmik ze zdjęć
ffmpeg -r 100 -f image2 -i img-%3d.png video.avi

# Wyciąganie klatek z filmiku i zlepianie w gif-a
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif

# Alternatywna wersja, też działa
ffmpeg -i video.avi -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - output2.gif
W animacji jest jakiś uszczerbek jakości, tak jakby brakowało wszystkich potrzebnych klatek. Być może uda mi się kiedyś rozwiązać tą zagadką i odpowiednio dobrać parametry, wtedy też o nich napiszę. Próbowałem także innych rozwiązań, bezskutecznie próbując wysterować opóźnienie pomiędzy kolejnymi klatkami.
# Gif generowany tylko przez convert (ImageMagic)
convert -dispose none -delay 1.5 $(ls -v *.png) -coalesce output3.gif

# Gif generowany tylko przez ffmpeg
ffmpeg -f image2 -framerate 100 -i img-%3d.png output4.gif