φυβλαςのβλογ
บล็อกของ 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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文