φυβλαςのβλογ
บล็อกของ phyblas



manim บทที่ ๑๘: การทำให้วัตถุสร้างใหม่ทุกเฟรมหรือเปลี่ยนแปลงไปตามวัตถุอื่น
เขียนเมื่อ 2021/03/12 18:23
แก้ไขล่าสุด 2021/09/28 16:42

ต่อจาก บทที่ ๑๗

ในบทที่ผ่านๆมาได้แสดงตัวอย่างการใช้ self.play() เพื่อทำให้วัตถุเปลี่ยนแปลงหรือเคลื่อนที่ไปหลายตัวอย่าง

แต่วัตถุที่เคลื่อนไหวหรือเปลี่ยนแปลงด้วย self.play() นั้นจะเคลื่อนไหวแค่ตัวมันเองเท่านั้น สำหรับในบทนี้จะพูดถึงวิธีต่างๆที่ทำให้การเปลี่ยนแปลงของวัตถุหนึ่งทำให้เกิดผลเพิ่มเติมไปด้วยตามมาโดยอัตโนมัติ




always_redraw()

หากมีวัตถุบางอย่างที่ต้องการจะให้วาดขึ้นมาใหม่ตลอดทุกเฟรมอาจทำการสร้างขึ้นมาโดยใช้ฟังก์ชัน always_redraw()

ตัวอย่างเช่นถ้าต้องการสร้างตัวหนังสือที่แสดงตำแหน่งของวัตถุ แต่ว่าวัตถุนั้นเคลื่อนที่ไปเรื่อยๆ แบบนี้ก็จำเป็นต้องดูค่าตำแหน่งขณะนั้นแล้วสร้างตัวหนังสือแสดงค่าใหม่ขึ้นมาในแต่ละเฟรม กรณีแบบนี้สามาถใช้ always_redraw() ได้

รูปแบบการใช้
mnm.always_redraw(ฟังก์ชันสร้างวัตถุ, อาร์กิวเมนต์ที่จะใส่ในฟังก์ชันนั้น)

ตัวอย่าง ลองสร้างจุดที่เคลื่อนที่ไปทางขวาเรื่อยๆ แล้วสร้างตัวหนังสือที่แสดงตำแหน่งแกน x ของจุด
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        wongklom = mnm.Circle(arc_center=np.array([-4,-2,0]),
                              radius=0.5,
                              fill_opacity=0.6,
                              color='#f3ddba')
        # ฟังก์ชันที่ใช้สร้างวัตถุนั้นใหม่ในทุกเฟรม
        def sailek(m):
            return mnm.Text(f'x = {m.get_x():.1f}',size=2.7,color='#79c582')
        # กำหนดให้เรียกใช้ฟังก์ชันนี้ทุกครั้งที่เปลี่ยนเฟรม
        lek = mnm.always_redraw(sailek,wongklom)
        self.add(lek)
        # เลื่อนวงกลมไปทางขวา
        self.play(
            wongklom.animate.shift(mnm.RIGHT*8),
            run_time=2
        )



อีกตัวอย่าง คราวนี้เพิ่มรายละเอียดฟังก์ชันให้ซับซ้อนขึ้น ให้แสดงตำแหน่งทั้งแกน x และ y และให้ตัวหนังสือทั้งเปลี่ยนไปด้วยและย้ายตำแหน่งไปเรื่อยๆด้วย
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        chut = mnm.Dot(np.array([2.5,-1.5,0]))
        def sailek(m):
            x,y = m.get_x(),m.get_y()
            # สร้างวัตถุตัวหนังสือ
            text = mnm.Text(f'x = {x:.1f}, y = {y:.1f}')
            # ปรับแต่งรายละเอียดตามเงื่อนไขค่า x และ y
            if(x<0): # ถ้า x น้อยกว่า 0 ให้เป็นสีม่วง
                text.set_color('#d6baf3')
            else: # ไม่เช่นนั้นให้เป็นสีแดง
                text.set_color('#c67982')
            if(y<0): # ถ้า y น้อยกว่า 0 ให้วางด้านบน
                text.next_to(chut,mnm.UP)
            else: # ถ้า ไม่เช่นนั้นให้วางด้านล่าง
                text.next_to(chut,mnm.DOWN)
            return text
        
        tamnaeng = mnm.always_redraw(sailek,chut)
        self.add(tamnaeng)
        self.play(
            chut.animate.move_to(np.array([-2,1,0])),
            run_time=2
        )






.add_updater()

หากมีวัตถุที่ต้องการให้มีการเปลี่ยนแปลงไปตลอดในทุกเฟรมโดยไม่ต้องคอยสร้างใหม่เรื่อยๆอาจทำได้โดยใช้เมธอด .add_updater()

เช่นถ้าแค่ต้องการให้ตัวหนังสือย้ายตำแหน่งไปเรื่อยๆตามตำแหน่งของจุด แบบนี้ไม่จำเป็นต้องสร้างใหม่ตลอดทุกครั้ง แต่แค่ให้เรียกใช้ .next_to() เพื่อให้ตามไปเรื่อยๆ

รูปแบบการใช้
วัตถุ.add_updater(ฟังก์ชันที่ต้องการให้เรียกใช้ทุกเฟรม)

ตัวอย่าง
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        chut = mnm.Dot(np.array([-2.5,-1.5,0]),radius=1)
        yuni = mnm.Text('อยู่นี่',size=2,color='#f3baf2')
        def yaipai(m):
            yuni.next_to(chut,mnm.LEFT)
        
        yuni.add_updater(yaipai)
        self.add(yuni)
        self.play(
            mnm.Rotate(chut,np.radians(-120),about_point=np.array([1,-1,0])),
            run_time=2
        )



วิธีการใช้จะคล้ายกับ always_redraw() แต่จะใช้ทำให้เกิดการเปลี่ยนแปลงที่ตัววัตถุที่มีอยู่แล้วโดยไม่ต้องสร้างใหม่




DecimalNumber

หากต้องการจะแสดงค่าตัวเลขบางอย่างซึ่งมีการเปลี่ยนแปลงไปเรื่อยๆ กรณีแบบนี้ใช้ DecimalNumber จะสะดวกกว่าใช้ Text หรือ Tex ซึ่งต้องคอยสั่งสร้างใหม่ด้วย always_redraw อยู่เรื่อยๆ

คลาส DecimalNumber เป็นวัตถุสำหรับแสดงค่าตัวเลขโดยสามารถเปลี่ยนแปลงค่าได้หลังจากที่สร้างแล้วได้โดยใช้เมธอด .set_value() ซึ่งเมธอดนี้ก็สามารถใช้กับ .animate เพื่อทำภาพเคลื่อนไหวแสดงการเปลี่ยนแปลงค่าได้

ตัวอย่างการใช้ DecimalNumber
import manimlib as mnm

class Manimala(mnm.Scene):
    def construct(self):
        lek = mnm.DecimalNumber(99.99,
                                num_decimal_places=3,
                                include_sign=True,
                                edge_to_fix=mnm.RIGHT,
                                font_size=200)
        self.add(lek)
        self.play(
            lek.animate.set_value(-7.654),
            run_time=1.5
        )



ค่าต่างๆที่ปรับได้มีดังนี้

คีย์เวิร์ดความหมายค่าตั้งต้น
num_decimal_placesจำนวนตำแหน่งทศนิยม3
include_signแสดงเครื่องหมายหรือไม่เมื่อเป็นค่าบวกFalse
edge_to_fixด้านที่จะตรึงตำแหน่งLEFT
font_sizeขนาดอักษร48


DecimalNumber สามารถใช้คู่กับ .add_updater() เพื่อให้เปลี่ยนแปลงตัวเลขไปตามค่าอะไรที่ต้องการได้

ตัวอย่าง ให้แสดงตำแหน่งจุด คล้ายกับตัวอย่างก่อนหน้านี้ที่ใช้ Text สร้าง แต่แค่ปรับค่าไปเรื่อยๆ ไม่ต้องใช้ always_redraw() เพื่อสร้างใหม่เรื่อยๆ
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        chut = mnm.Dot(np.array([-4,2,0]),radius=0.5,color='#ffc1c1')
        lek = mnm.DecimalNumber(0,num_decimal_places=4,font_size=130)
        def prapkha(m):
            m.set_value(chut.get_x())
        
        lek.add_updater(prapkha)
        self.add(lek)
        self.play(
            chut.animate.shift(mnm.RIGHT*8),
            run_time=2
        )



อีกตัวอย่างหนึ่ง แสดงการปรับทั้งตัวเลขและตำแหน่งไปพร้อมๆกัน
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        chut = mnm.Dot(np.array([-1,0,0])) # จุด
        # แถบข้อความตัวเลขที่ใช้บอกตำแหน่งพิกัด x,y
        lek = mnm.VGroup(mnm.Text('x=',color='#c3f3ba'),
                         mnm.DecimalNumber(0,include_sign=True),
                         mnm.Text(' y=',color='#f2f3ba'),
                         mnm.DecimalNumber(0,include_sign=True))
        lek.next_to(chut,mnm.UP)
        lek.arrange(mnm.RIGHT)
        # ฟังก์ชันสำหรับใช้ใน .add_updater()
        def prapkha(m):
            m.next_to(chut,mnm.UP) # วางตำแหน่งไว้บนจุด
            m[1].set_value(chut.get_x()) # ค่าตำแหน่งแกน x
            m[3].set_value(chut.get_y()) # ค่าตำแหน่งแกน y
        # ตั้งให้ฟังก์ชันนี้ทำงานเพื่อปรับค่าทุกครั้งที่ขึ้นเฟรมใหม่
        lek.add_updater(prapkha)
        self.add(lek)
        self.play(
            chut.animate.shift(mnm.DR*2),
            run_time=1.5
        )






Integer

คลาส Integer เป็นคลาสย่อยของ DecimalNumber ในกรณีที่ num_decimal_places=0 ก็คือไม่มีเลขทศนิยม เป็นจำนวนเต็ม วิธีการใช้ก็เหมือนกัน

ตัวอย่าง
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        lek = mnm.Integer(0,font_size=225,edge_to_fix=mnm.RIGHT)
        lek.move_to(np.array([3,0,0]))
        def prapkha(m):
            m.set_value(lek.get_x())
        
        lek.add_updater(prapkha)
        self.add(lek)
        self.play(
            lek.animate.shift(mnm.LEFT*6),
            run_time=1.5
        )






always()

ฟังก์ชัน always() เอาไว้ใช้เขียนย่อแทน .add_updater() ในบางกรณี

โดยวิธีการใช้ก็คือตั้งให้มีการใช้ฟังก์ชันอะไรบางอย่างตลอดทุกเฟรม

รูปแบบการใช้งาน
mnm.always(เมธอดของวัตถุ, อาร์กิวเมนต์ที่จะใส่ในเมธอดนั้น)

ตัวอย่าง สร้างข้อความที่ย้ายตำแหน่งไปตามสี่เหลี่ยม
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        siliam = mnm.Rectangle(7,4,color='#d1ffc1')
        text = mnm.Text('สี่เหลี่ยมผืนผ้า',color='#f7c1ff')
        mnm.always(text.next_to,siliam,mnm.UP)
        # แทนที่จะเขียน text.add_updater(lambda m:m.next_to(siliam,mnm.UP))
        self.add(text)
        self.play(
            mnm.FadeOutToPoint(siliam,np.array([-3.5,-2,0])),
            run_time=1.5
        )



จะเห็นว่าเป็นการเขียนแทน .add_updater() ทำให้ดูสั้นลง แต่ผลการทำงานที่ได้ก็เหมือนกัน




f_always()

f_always() ก็เป็นอีกฟังก์ชันที่สามารถใช้เพื่อเขียนย่อแทน .add_updater() ได้ เช่นเดียวกับalways()

ข้อแตกต่างระหว่าง always() กับ f_always() ก็คืออาร์กิวเมนต์ตัวที่ ๒​ ของ f_always() ที่จะต้องใส่นั้นจะเป็นฟังก์ชัน ที่จะให้ถูกนำมาเรียกใช้อีกที แต่ของ always() คือตัวค่าที่จะใส่

รูปแบบการใช้งาน
mnm.f_always(เมธอดของวัตถุ, ฟังก์ชันที่จะให้เรียกใช้เพื่อให้คืนค่าให้เมธอดนั้น)

ตัวอย่าง สร้างค่าตัวเลขบอกขนาดความกว้างของวงรี ซึ่งเปลี่ยนค่าและย้ายตำแหน่งไปตามวงรีตลอด
import manimlib as mnm
import numpy as np

class Manimala(mnm.Scene):
    def construct(self):
        wongri = mnm.Ellipse(width=6,height=3,color='#c1caff',stroke_width=7)
        lek = mnm.DecimalNumber(0,font_size=100)
        mnm.always(lek.next_to,wongri,mnm.UP)
        # แทน lek.add_updater(lambda m:m.next_to(wongri,mnm.UP))
        mnm.f_always(lek.set_value,wongri.get_width)
        # แทน lek.add_updater(lambda m:m.set_value(wongri.get_width()))
        self.add(lek)
        self.play(
            mnm.FadeOutToPoint(wongri,np.array([-3,-1.5,0])),
            run_time=1.5
        )





อ่านบทถัดไป >> บทที่ ๑๙





-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> manim

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文