รูปร่าง (สี่เหลี่ยม) ทำงานในต้นไม้สี่ทิศได้อย่างไร


10

ฉันถูกบอกว่าต้นไม้รูปสี่เหลี่ยมเป็นโครงสร้างข้อมูลที่เหมาะสำหรับเกมของฉัน แต่ฉันมีปัญหาในการทำความเข้าใจว่ารูปร่างทำงานอย่างไรภายในต้นไม้รูปสี่เหลี่ยม

ฉันทำสิ่งนี้ใน JavaScript แต่ฉันคิดว่าคำถามเหล่านี้สามารถใช้ได้กับต้นไม้สี่ต้นในภาษาใด ๆ

ผมคิดว่าผมเข้าใจส่วนใหญ่วิธีการขั้นพื้นฐาน (x, y) จุดและแทรกจุดงานในต้นไม้รูปสี่เหลี่ยมและที่ฉันสามารถทำมันบนกระดาษ

นี่คือJSfiddleของการทดสอบของฉันกับจุด

จุดต้นไม้รูปสี่เหลี่ยม

นอกเหนือจากกรณีหนึ่งการทดสอบของฉันกับคะแนนทำงานตามที่คาดไว้

แต่ความสับสนของฉันเริ่มต้นเมื่อรูปร่างเช่นสี่เหลี่ยมมีส่วนร่วม เมื่อคุณดึงข้อมูลจากต้นไม้รูปสี่เหลี่ยมด้วยรูปร่างมันจะตรวจสอบแต่ละจุดของรูปร่างและโหนดใดที่มันตกลงไป และการแทรกรูปร่างยังทำงานได้อย่างไรเมื่อมันยอมรับพารามิเตอร์ (x, y, width, height) สำหรับแต่ละรูปร่าง มันใช้ความกว้าง / ความสูงจากจุดเริ่มต้นในการคำนวณจุดมุมอื่น ๆ ซึ่งจะถูกแจกจ่ายไปยังโหนดที่เหมาะสม? หากรูปร่างที่แทรกเข้าไปมีสี่โหนดข้อมูลของรูปร่างนั้นจะถูกบันทึกลงในทั้งสี่โหนดหรือไม่

และเมื่อวิธีการดึงข้อมูลยอมรับรูปร่างเป็นพารามิเตอร์ (x, y, ความกว้างความสูง) สิ่งที่เกิดขึ้นจริง? มันเป็นครั้งแรกหรือไม่ที่จะเห็นว่ารูปร่างของโหนดนั้นจะขยายไปถึงถ้ามันถูกแทรกเข้าไปแล้วดึงวัตถุทั้งหมดของโหนดเหล่านั้นหรือไม่?

ฉันมีJSfiddle ทำงานกับรูปร่างแต่ฉันสับสนอย่างมากกับผลลัพธ์ของการทดสอบของฉัน ฉันได้รับวัตถุที่ซ้ำกันที่ถูกส่งคืน!

รูปทรงต้นไม้รูปสี่เหลี่ยม

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

ถ้าฉันต้องการส่งคืนคะแนนทั้งหมดในต้นไม้รูปสี่เหลี่ยมฉันจะทำอย่างไร วิธีการดึงข้อมูลโดยใช้รูปร่างขนาดทั้งหมดของขอบเขต? ตัวอย่างเช่นดึง (0, 0, canvas.width, canvas.height)?

ห้องสมุด JavaScript quadtreeฉันใช้ได้รับการเรียกจากแหล่งอื่น ๆ อีกมากมายดังนั้นผมถือว่าการดำเนินงานที่เกิดขึ้นจริงที่ถูกต้องและมีชื่อเสียง

ฉันคิดว่าความสับสนมากมายของฉันอาจเกิดจากความเข้าใจผิดของคำศัพท์ต้นไม้สี่ เช่นทำไมพวกเขาพูดขอบเขตแทนมิติเมื่อ "จุด" มีพารามิเตอร์ความกว้าง / ความสูงเช่นกัน? มันเป็นเรื่องของการประชุม / มือสั้นหรือพวกเขามีแนวคิดที่แตกต่างอย่างสิ้นเชิง?

ขอบคุณที่สละเวลา!


พวกมันถูกเก็บไว้ในต้นไม้รูปสี่เหลี่ยมตามปกติโดยตำแหน่งของพวกเขา โดยทั่วไปแล้วจะอยู่ที่กึ่งกลาง แต่อาจอยู่ในมุมที่คุณกำหนดไว้ คำถามของคุณอาจซ้ำกับคำถามนี้: QuadTree: เก็บเฉพาะจุดหรือภูมิภาค
MichaelHouse

ฉันอ่านคำถามนั้นแล้ว แต่ฉันยังไม่เข้าใจคำตอบทั้งหมด :( "คุณต้องเก็บมันไว้ในโหนดที่เล็กที่สุดที่มีอยู่อย่างสมบูรณ์ - แม้ว่าจะเกินความจุ (ใช้คอนเทนเนอร์ที่ปรับขนาดได้)" - เมื่อเขา กล่าวว่าโหนดที่เล็กที่สุดที่ COMPLETELY บรรจุถ้าวัตถุมีขนาดใหญ่มากโหนดนั้นมักจะไม่ใช่โหนดใบหรือในโหนดที่สูงกว่าที่ประกอบด้วยโหนดอื่นเท่านั้นดูเหมือนจะไม่ถูกต้อง ฉันเนื่องจากนั่นหมายความว่าโหนดที่มีนั้นจะมี 1 leaf และ 4 โหนดที่เล็กกว่าอยู่ภายใน
user2736286

1
@ user2736286 ถ้าคุณดูรหัสสำหรับ quadtree lib (มันไม่ยาวมาก) คุณสามารถเห็นมันเก็บสี่เหลี่ยมในโหนดระดับสูงกว่าเพื่อป้องกันไม่ให้เส้นขอบโหนดคร่อม ดูการอ้างอิงไปยัง_stuckChildrenเขตข้อมูลในรหัส คุณยังสามารถเห็นสิ่งนี้ได้ในตัวอย่าง "การดึงไอเท็มที่มีขอบเขต" - มันจะเน้นสีแดงของโหนดที่ข้ามขอบของโหนดที่คุณคลิกเข้าไปจนถึงรูตโหนด
นาธานรีด

@ user2736286 นอกจากนี้ lib quadtree ยังมีข้อบกพร่องในการดึงรายการด้วยขอบเขต - ถ้าคุณให้ rect rects ของแบบสอบถามที่เลาะข้ามขอบโหนดบางโหนดมันจะไม่ส่งกลับ rects ทั้งหมดในโหนดที่แบบสอบถามได้สัมผัส คุณสามารถดูสิ่งนี้ได้อย่างง่ายดายใน "การดึงรายการที่มีขอบเขต" เช่นกันโดยคลิกใกล้กับขอบโหนด
นาธานรีด

@NathanReed ก่อนหน้านี้ฉันพยายามเข้าใจรหัส แต่ฉันไม่ชำนาญพอที่จะเข้าใจได้โดยไม่ต้องมีแนวคิดพื้นฐาน ขอบคุณรหัสหลอกของ John McDonald ตอนนี้ฉันเข้าใจวิธีการแทรกสี่เหลี่ยมลงในโหนด แต่ฉันคิดว่าฉันยังไม่ชัดเจนเกี่ยวกับวิธีการดึงข้อมูล สำหรับ "การดึงไอเท็มที่มีขอบเขต" - ฉันสับสนโดยตัวอย่าง เช่นเมื่อฉันคลิก rect ที่พอดีกับโหนดที่เล็กที่สุดอย่างใดอย่างหนึ่งทำไม rects อื่นทั้งหมดเหล่านี้อยู่นอกโหนดนั้นจึงเน้นด้วย
2736286

คำตอบ:


10

โดยทั่วไปแล้วQuadtreesจะเก็บและดึงสี่เหลี่ยม จุดเป็นกรณีเฉพาะที่ความกว้างและความสูงเป็นศูนย์ ตรรกะต่อไปนี้ใช้เพื่อค้นหาที่อยู่อาศัยสำหรับสี่เหลี่ยมใหม่ในต้นไม้เริ่มต้นด้วยโหนดรูต:

void Store(Rectangle rect)
{
    if(I have children nodes)
    {
        bool storedInChild = false;
        foreach(Node childNode in nodes)
        {
            if(this rectangle fits entirely inside the childNode bounds)
            {
                childNode.Store(rect);   // Go deeper into the tree
                storedInChild = true;
                break;
            }
        }
        if(not storedInChild)
        {
            Add this rectangle to the current node
        }
    }
    else
    {
        Add this rectangle to the current node
    }
}

โปรดทราบว่าสี่เหลี่ยมสามารถเก็บไว้ที่ระดับความลึกใด ๆ ไม่จำเป็นต้องเป็นรูปสี่เหลี่ยมของใบไม้ หากสี่เหลี่ยมผืนผ้าเลาะเลียบไปตามขอบเขตที่ระดับรูทรูปสี่เหลี่ยมรูทจะจัดเก็บสี่เหลี่ยมผืนผ้า

ตัวอย่างเช่นสี่เหลี่ยมจัตุรัสสีแดงมีค่าเทียบเท่ากับพารามิเตอร์ที่ฉันกำลังเข้าสู่วิธีการดึงของฉัน ฉันคิดว่าเนื่องจากสี่เหลี่ยมสีแดงนี้ครอบคลุมทั้งสี่โหนดมันควรส่งคืนวัตถุทุกชิ้นในต้นไม้รูปสี่เหลี่ยม! แต่มันไม่ได้และฉันมีปัญหาในการหาเหตุผลเข้าข้างตนเองว่ามันจะกลับมา

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

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

แก้ไข

การสืบค้นมักจะทำดังนี้:

List<Rectangle> Query(Rectangle queryRect)
{
    List<Rectangle> results;
    foreach(Node childNode in children)
    {
        if(queryRect intersects with childNode bounds)
        {
            results += childNode.Query(queryRect);   // Dig deeper into the tree
        }
    }

    foreach(Rectangle I have stored in this quad)
    {
        if(queryRect intersects with rect)  // Your library doesn't do this
        {
            results += rect;
        }
    }

    return results;
}

อย่างไรก็ตามในห้องสมุดของคุณดูเหมือนจะไม่ตรวจสอบว่าสี่เหลี่ยมที่มันส่งกลับมาตัดกับคิวรีหรือไม่ดังนั้นคุณจะต้องเพิ่มด้วยตัวเอง


หลังจากดูรายละเอียดเพิ่มเติมในไลบรารีของคุณไลบรารีของคุณจะส่งคืนรายการของตัวเลือกการชนกันทั้งหมดที่เป็นไปได้สำหรับสี่เหลี่ยมแบบสอบถามที่คุณระบุ ดูเหมือนว่าเป็นหน้าที่ของคุณในการเปรียบเทียบสี่เหลี่ยมคิวรีของคุณกับผู้สมัครแต่ละคนเพื่อดูว่ามีการชนจริงหรือไม่ ห้องสมุดส่วนใหญ่ทำขั้นตอนสุดท้ายให้คุณและเพียงแค่คืนค่าการชนจริงด้วยพื้นที่แบบสอบถาม ดังนั้นสิ่งที่คุณต้องทำคือเพิ่มตรรกะบางอย่างในretrieveวิธีการของคุณเพื่อตัดรายการ คุณสามารถดูสิ่งที่กำลังทำอยู่ได้ชัดเจนยิ่งขึ้นที่นี่: mikechambers.com/html5/javascript/QuadTree/examples/…
John McDonald

1
@ user2736286 ในกรณีที่คุณไม่ได้รับมันเป็นสิ่งสำคัญที่จะต้องทราบการเรียกซ้ำในฟังก์ชั่น Query และ Store ที่ JohnMcDonald เขียน มันจะไม่สมเหตุสมผลถ้าคุณไม่เข้าใจส่วนนั้น สำหรับทั้งสองการเรียกซ้ำแต่ละครั้งจะลึกเข้าไปในต้นไม้เช่นออกไปตามกิ่งและในที่สุดก็กลายเป็นใบไม้
TASagent

ขอบคุณ John รหัสหลอกของคุณมีประโยชน์มาก เมื่อคุณพูดว่า "if (queryRect ตัดกับขอบเขตขอบเขตโหนดเด็ก)" หมายความว่าโดยทั่วไปแล้วถ้า queryRect อยู่ภายในขอบเขต - บางส่วนหรือทั้งหมดหรือไม่ เพียงต้องการให้ชัดเจน 100% นอกจากนี้ในตัวอย่าง "เรียกคืนรายการตามขอบเขต" ที่กล่าวถึงฉันมีภาพผลการคลิกของฉัน Picจุดสีฟ้าคือที่ที่ฉันคลิก เหตุใดจึงไม่เพียง แต่สอง rectanges ภายในโหนดนั้น hightlighted ทำไมสี่เหลี่ยมที่อยู่ห่างไกลจากไฮไลต์เช่นกัน? ฉันคิดว่านั่นเป็นสิ่งที่ทำให้ฉันสับสนจริงๆ
2736286

บางส่วนหรือเต็ม หากสัมผัสทั้งสองหรืออย่างใดอย่างหนึ่งจะถูกบรรจุโดยสมบูรณ์อื่น ๆ คุณต้องการที่จะอ่านวิกิบนQuadtreesแต่ฉันเอารูปของคุณไปแล้วและสีมันเป็นรหัสเพื่อเป็นตัวแทนขอบเขตเด็กที่แตกต่างกัน สี่เหลี่ยมสีน้ำเงินทั้งหมดตัดกันโดยมีขอบเขตของเด็กสี่คนแรกจากนั้นสีม่วงแดงจะลึกลงไปหนึ่งชั้นและสีเขียวก็ลึกลงไปหนึ่งชั้น สี่เหลี่ยมสีแดงตัดกับสี่เหลี่ยมที่ถูกคลิก คุณส่งคืนไลบรารีทั้งหมดของขอบเขตเด็กพร้อมด้วยทั้งหมดใน quad child สุดท้าย (สีแดง): i.imgur.com/InB8KBj.png
John McDonald

ตกลงดังนั้นฉันพยายามอย่างดีที่สุดที่จะอ่านซอร์สโค้ดของ lib และในที่สุดฉันก็เข้าใจพฤติกรรมของ stickChildren และสิ่งที่พิเศษเหล่านั้นในรูปของฉันก็คือ StickChildren แต่เดิมฉันคิดว่า rects ใด ๆ ที่ครอบคลุมหลายโหนดจะมีการทำซ้ำข้อมูลและแทรกในแต่ละโหนดที่เล็กกว่า ตอนนี้ฉันรู้แล้วว่าเด็กติดไม่ได้ถูกแทรกในโหนดมันมี แต่เพียงอยู่ในหนึ่งโหนดที่มีมันซึ่งเป็นเหตุผลที่ฉันต้องตรวจสอบทุกโหนดแม่และไม่เพียงโหนดที่เล็กที่สุดที่มี rect แบบสอบถามของฉัน ขอบคุณสำหรับรูป; มันสมเหตุสมผลมากกว่าตอนนี้ :)
user2736286
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.