บางครั้งเราอาจต้องการสร้างวัตถุที่มีลักษณะเหมือนเดิมซ้ำๆหรือคล้ายจากเดิมหลายอัน
โดยปกติเราสามารถใช้การวนซ้ำ for หรือ while เพื่อสร้างวัตถุที่ต้องการขึ้นมากี่อันก็ได้ตามที่ต้องการอยู่แล้ว
แต่ว่าบางครั้งการซ้ำกระบวนการสร้างเดิมอยู่หลายๆครั้งนั้นเป็นอะไรที่ต้องใช้ เวลา ยิ่งวัตถุมีรูปร่างซับซ้อน กว่าจะทำออกมาได้อันหนึ่งก็ยิ่งนาน
กรณีที่มีวัตถุที่ต้องการวัตถุที่ซ้ำๆกันหรือสามารถแปลงรูปจากรูปร่างที่มีอยู่ ได้โดยง่าย เราสามารถใช้วิธีการทำสำเนาของวัตถุได้ โดยฟังก์ชันที่ใช้ก็คือ duplicate()
การใช้งานนั้นง่ายมาก แค่กดเลือกที่วัตถุที่ต้องการทำการสำเนาแล้วก็พิมพ์ mc.duplicate() เท่านี้ก็ได้วัตถุชิ้นเดิมอีกอันซึ่งอยู่ในตำแหน่งเดียวกัน จากนั้นก็กดย้ายมันไปไว้ในตำแหน่งอื่นตามที่ต้องการ เช่น
mc.polySphere(r=1)
for i in range(10):
mc.duplicate()
mc.move(i*2,i*2,0)
(หมายเหตุ ภาพในบทนี้มีการปรับแสงไฟโดยใช้ไฟสามสีจาก
บทที่แล้ว หากไม่มีแสงวัตถุจะเป็นสีเทาตามปกติ)
หรือถ้าใส่ชื่อของวัตถุที่ต้องการทำสำเนาลงไปเป็นอาร์กิวเมนต์ก็สามารถสำเนาวัตถุได้แม้จะไม่ได้เลือกอยู่
วัตถุที่ถูกทำสำเนาขึ้นมาใหม่สามารถตั้งชื่อได้เช่นกันโดยใส่แฟล้ก n (name) ถ้าไม่ตั้งจะถูกตั้งให้โดยอัตโนมัติให้สัมพันธ์กับวัตถุต้นฉบับ
เพื่อแสดงให้เห็นว่าการสำเนาวัตถุขึ้นมาใหม่นั้นเร็วกว่าการสร้างขึ้นมาใหม่ เราจะลองทดสอบความต่างของเวลาที่ใช้ดูได้ด้วยฟังก์ชัน time ซึ่งอยู่คำสั่ง time ซึ่งเป็นชุดคำสั่งมาตรฐานของไพธอนที่มีอยู่แล้ว
วิธีใช้
import time # ประกาศนำเข้าชุดคำสั่ง
t0 = time.time() # เวลาเริ่มต้น
<กลุ่มคำสั่งที่ต้องการจับเวลา>
print(time.time() - t0) # พิมพ์เวลาสุดท้ายลบด้วยเวลาเริ่มต้น
เราลองทดสอบโดยการใช้จับเวลาเปรียบเทียบระหว่างใช้การสำเนากับใช้การวนซ้ำเพื่อสร้างใหม่
ลองสร้างวัตถุทรงกลมที่รวมกันเป็นกลุ่ม ๓๘ ลูก โดยเริ่มแรกจับเวลาที่ใช้สร้างด้วยวิธีสร้างซ้ำแล้วก็พิมพ์เวลาออกมา จากนั้นก็สร้างเหมือนเดิมอีกด้วยวิธีการทำสำเนา แล้วก็พิมพ์เวลาออกมาอีก จากนั้นลองเทียบกันดู
import time
k = [1,4,8,12,8,4,1]
# ใช้วิธีสร้างซ้ำ
t0 = time.time()
g = [] # สร้างลิสต์ที่เก็บชื่อของวัตถุ
for j in range(7):
for i in range(k[j]):
g += mc.polySphere(r=1,ch=0) # สร้างทรงกลมใหม่ พร้อมเพิ่มชื่อเข้าลิสต์
mc.move(0,2,0) # ย้ายไปไว้ตำแหน่งขั้วเหนือ
mc.rotate(j*30,360/k[j]*i,0,p=[0,0,0]) # หมุนตามแนวละติจูดและลองจิจูด
mc.group(g) # รวมกลุ่ม
print(time.time() - t0) # พิมพ์เวลาที่ใช้
# ใช้วิธีทำสำเนาไปเรื่อยๆ
t0 = time.time()
g = mc.polySphere(r=1,ch=0) # สร้างทรงกลมลูกแรก พร้อมเก็บชื่อลิสต์
mc.move(0,2,0)
for j in range(1,7):
for i in range(k[j]):
g += mc.duplicate() # ทำสำเนา พร้อมเพิ่มชื่อเข้าลิสต์
mc.rotate(j*30,360/k[j]*i,0,p=[0,0,0])
mc.group(g) # รวมกลุ่ม
print(time.time() - t0) # พิมพ์เวลาที่ใช้
จะได้ตัวเลขเวลาออกมาสองค่า จะเห็นว่าเลขตัวแรกจะมากกว่า นั่นคือวิธีการทำสำเนานั้นใช้เวลาน้อยกว่า ดังนั้นจึงช่วยประหยัดเวลาในการสร้างได้ แม้ว่าผลลัพธ์สุดท้ายจะเหมือนกันก็ตาม
การสำเนานั้นไม่เพียงทำกับตัววัตถุทีละชิ้นเท่านั้น ยังสามารถสำเนาวัตถุพรุ้อมกันหลายชิ้น หรืออาจสำเนากลุ่มของวัตถุก็ได้
ปกติเวลาที่ใช้ duplicate() ฟังก์ชันจะคืนค่าลิสต์ของวัตถุที่ถูกสำเนากลับคืนมาทั้งหมด กรณีที่สำเนากลุ่ม จะได้ทั้งชื่อของกลุ่มและชื่อของวัตถุในกลุ่มมาพร้อมกัน
ลองเทียบอีกตัวอย่างหนึ่งที่ซับซ้อนกว่าเดิม คราวนี้ลองเอากลุ่มทรงกลมที่ได้ออกมานั้นมานั้นมาสร้างใหม่ซ้ำๆแล้วรวมเป็นก ลุ่มที่ใหญ่ขึ้นไปอีก
import time
k = [1,4,8,12,8,4,1]
# ใช้วิธีสร้างซ้ำ
t1 = time.time()
gg = [] # สร้างลิสต์ที่เก็บชื่อของกลุ่มทรงกลม
for n in range(7): # วงใหญ่ (วนซ้ำเพื่อสร้างกลุ่มของทรงกลมที่ถูกสร้างในวงย่อย)
for m in range(k[n]):
g = [] # สร้างลิสต์ที่เก็บชื่อของทรงกลมย่อย
for j in range(7): # วงย่อย (สร้างทรงกลมย่อย)
for i in range(k[j]):
g += mc.polySphere(r=1,ch=0) # สร้างทรงกลมย่อยพร้อมเก็บชื่อเข้าลิสต์
mc.move(0,2,0)
mc.rotate(j*30,360/k[j]*i,0,p=[0,0,0])
gg += [mc.group(g)] # รวมทรงกลมย่อยเป็นกลุ่มพร้อมเพิ่มชื่อเข้าลิสต์
mc.move(0,6,0)
mc.rotate(n*30,360/k[n]*m,0,p=[0,0,0])
mc.group(gg) # รวมกลุ่มทรงกลมเป็นใหญ่
print(time.time() - t1) # พิมพ์เวลาที่ใช้
# ใช้วิธีทำสำเนาไปเรื่อยๆ
t1 = time.time()
g = mc.polySphere(r=1,ch=0) # สร้างทรงกลมย่อยลูกแรก
mc.move(0,2,0)
for j in range(1,7):
for i in range(k[j]):
g += mc.duplicate() # สำเนาทรงกลมย่อย
mc.rotate(j*30,360/k[j]*i,0,p=[0,0,0])
gg = [mc.group(g)] # รวมกลุ่มเป็นกลุ่มทรงกลมกลุ่มแรก พร้อมเก็บชื่อเข้าลิสต์ที่เก็บชื่อกลุ่มทรงกลม
mc.move(0,6,0)
for j in range(1,7):
for i in range(k[j]):
gg += [mc.duplicate()[0]] # สำเนากลุ่มทรงกลม พร้อมเก็บชื่อเข้าลิสต์ โดย [0] ในที่นี้หมายถึงเอาเฉพาะชื่อของกลุ่มซึ่งเป็นชื่อแรกที่ฟังก์ชันคืนกลับมา
mc.rotate(j*30,360/k[j]*i,0,p=[0,0,0])
mc.group(gg) # รวมกลุ่มทรงกลมเป็นกลุ่มใหญ่
print(time.time() - t1) # พิมพ์เวลาที่ใช้
จะเห็นว่าใช้การทำสำเนานั้นให้ผลเป็นเวลาที่เร็วกว่า อีกทั้งโค้ดดูง่ายกว่า ไม่ต้องสร้างวงวนซ้ำซ้อนกัน
อนึ่ง หากลองไปดูที่ชื่อของกลุ่มที่ถูกสำเนาขึ้นมาจะเห็นว่าชื่อเหมือนกันหมดทุก กลุ่ม เช่นในกลุ่ม group40 ถึง group 77 จะมีวัตถุชื่อ pSphere1445 ถึง pSphere1482 อยู่เหมือนกันหมด
หากมีวัตถุชื่อซ้ำกันจะมีปัญหาเวลาที่ อ้างอิงถึงวัตถุนั้น ถ้าหากพิมพ์แค่ชื่อวัตถุนั้นลงไปเช่น mc.select('pSphere1445') ก็จะพบว่าโปรแกรมขัดข้องเพราะไม่รู้ว่าหมายถึงชิ้นไหน
แต่สำหรับวัตถุที่แม้จะชื่อซ้ำกันแต่อยู่สังกัดคนละกลุ่มกันก็สามารถแยกแยะได้โดย พิมพ์ชื่อกลุ่มที่สังกัดนำหน้าไว้แล้วตามด้วย | แล้วค่อยตามด้วยชื่อวัตถุนั้น เช่นในกรณีนี้พิมพ์เป็น
mc.select('group40|pSphere1445')
ก็จะเป็นการเลือกวัตถุ pSphere1445 ที่อยู่ในกลุ่ม group40 ไม่ใช่ที่อยู่ในกลุ่มอื่น สามารถแยกแยะกันได้ชัดเจนไม่มีปัญหาอะไร
ดังนั้นโดยทั่วไปอาจไม่ต้องคิดมากเรื่องที่ชื่อซ้ำ ถือเป็นเรื่องปกติและยังทำให้สะดวกในการอ้างอิงด้วย
อย่างไรก็ตามตอนที่สำเนาหากต้องการเปลี่ยนชื่อวัตถุที่อยู่ในกลุ่มให้ไม่ซ้ำกันก็ สามารถทำได้โดยเพิ่มแฟล็ก rc (renameChildren) เป็น rc=1 แล้วชื่อวัตถุก็จะถูกเปลี่ยนให้ไม่ซ้ำกัน
ลองสร้างใหม่โดยใช้โค้ด ตัวอย่างที่แล้ว โดยแค่เพิ่มแฟล็ก rc=1 ลงในฟังก์ชัน duplicate() เป็น mc.duplicate(rc=1) ทั้งสองที่ จะได้ผลเหมือนเดิมแต่ต่างกันแค่ชื่อของวัตถุ
นอกจากนี้การสำเนาไม่ได้ทำได้เพียงแค่วัตถุที่จับต้องได้เท่านั้น ยังสามารถใช้กับโหนดอย่างอื่นด้วย เช่นเท็กซ์เจอร์หรือวัสดุ ฯลฯ ได้ด้วยเช่นกัน
duplicate() เป็นคำสั่งที่มีประโยชน์มากมาย น่าจะได้ใช้กันต่อไปอีกเยอะ
อ้างอิง