การเคลื่อนย้ายเรือไปมาระหว่างดาวเคราะห์ทั้งสองไปพร้อมกับเบซิเยร์หายไปสมการบางส่วนสำหรับการเร่งความเร็ว


48

ตกลงฉันโพสต์สิ่งนี้ไว้ที่ math.stackechange.com แต่ไม่ได้รับคำตอบใด ๆ :(

ก่อนอื่นนี่คือภาพปัญหาของฉันคำอธิบายต่อไปนี้:

ข้อความแสดงแทน

ดังนั้นฉันจึงได้รับคะแนนและค่าทั้งหมด

เรือเริ่มเคลื่อนที่ไปรอบ ๆ ดาวเคราะห์ด้านซ้ายP1ด้วยS=0.27 Degreesต่อ gametick เมื่อมันมาถึงPoint Aมันจะเริ่มตามโค้ง bezier จนกระทั่งมันมาถึงPoint Dจากนั้นมันจะเดินทางไปรอบ ๆ ดาวเคราะห์ที่ถูกต้องP2ด้วยS=0.42 Degreesเห็บต่อเกม ความแตกต่างSคือเพื่อให้การเดินทางด้วยความเร็วการเคลื่อนที่เดียวกันรอบ ๆ ดาวเคราะห์

จนถึงตอนนี้ฉันก็เริ่มทำงานแล้วตอนนี้ปัญหาของฉัน

เมื่อใดS P1และS P2แตกต่างกันมากเรือรบกระโดดข้ามความเร็วทั้งสองเมื่อถึงที่หมายปลายทางซึ่งดูไม่ดีนัก ดังนั้นผมจึงต้องเร่งเรือระหว่างPoint AและPoint DจากไปS P1S P2

สิ่งที่ฉันขาดไปคือสีม่วงนั่นคือ:

  • วิธีในการคำนวณเห็บที่ใช้เรือเคลื่อนที่ไปตาม bezier โดยพิจารณาจากความเร่ง

  • และวิธีหาตำแหน่งบนเส้นโค้งเบซิเยร์ตาม T ลองพิจารณาความเร่งอีกครั้ง

ATM ฉันคำนวณความยาวของ bezier โดยการคำนวณระยะทางระหว่างNจุดต่างๆ ดังนั้นสิ่งที่ฉันคิดว่าฉันต้องการคือวิธีการปรับขนาดที่Tฉันจำเป็นต้องใช้ในการคำนวณ bezier ของฉันตามการเร่งความเร็ว


2
สามารถหาคำตอบได้ดี ฉันขอแนะนำให้คุณโพสต์สิ่งที่คุณค้นพบเป็นคำตอบสำหรับคำถามของคุณ
bummzack

คำตอบ:


83

ตกลงฉันได้ทุกอย่างทำงานมันใช้เวลาตลอดไปดังนั้นฉันจะโพสต์โซลูชั่นรายละเอียดของฉันที่นี่
หมายเหตุ: ตัวอย่างโค้ดทั้งหมดอยู่ใน JavaScript

ดังนั้นขอแบ่งปัญหาออกเป็นส่วนพื้นฐาน:

  1. คุณต้องคำนวณความยาวของและจุดระหว่าง0..1บนเส้นโค้งเบซิเยร์

  2. ตอนนี้คุณต้องปรับสเกลของคุณTเพื่อเร่งความเร็วเรือจากความเร็วหนึ่งไปอีกความเร็วหนึ่ง

ทำให้ Bezier ถูกต้อง

การค้นหารหัสบางอย่างสำหรับการวาดเส้นโค้ง Bezier นั้นง่ายมีหลายวิธีที่แตกต่างกัน แต่หนึ่งในนั้นคือDeCasteljau Algorithmแต่คุณสามารถใช้สมการสำหรับเส้นโค้งเบซิเยร์ลูกบาศก์ได้:

// Part of a class, a, b, c, d are the four control points of the curve
x: function (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
           + 3 * ((1 - t) * (1 - t)) * t * this.b.x
           + 3 * (1 - t) * (t * t) * this.c.x
           + (t * t * t) * this.d.x;
},

y: function (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
           + 3 * ((1 - t) * (1 - t)) * t * this.b.y
           + 3 * (1 - t) * (t * t) * this.c.y
           + (t * t * t) * this.d.y;
}

ด้วยวิธีนี้ได้ในขณะนี้สามารถวาดเส้นโค้งเบซิเยร์โดยการเรียกxและyด้วยtซึ่งมีตั้งแต่0 to 1ลองมาดู:

ข้อความแสดงแทน

เอ่อ ... นั่นไม่ได้เป็นการกระจายจุดอย่างสม่ำเสมอจริงๆเหรอ?
เนื่องจากลักษณะของเส้นโค้งเบซิเยร์จุดที่0...1แตกต่างกันarc lenghtsดังนั้นส่วนที่อยู่ใกล้จุดเริ่มต้นและจุดสิ้นสุดจึงยาวกว่าจุดที่อยู่ใกล้กับกึ่งกลางของเส้นโค้ง

การแมป T อย่างสม่ำเสมอบนการกำหนดพารามิเตอร์ความยาวส่วนโค้งของ AKA

แล้วจะทำอย่างไรดี? ในแง่ง่ายเราจำเป็นต้องมีฟังก์ชั่นในการแมปของเราTลงบนส่วนtโค้งเพื่อให้T 0.25ผลลัพธ์ของเราอยู่tที่25%ความยาวของส่วนโค้ง

เราจะทำอย่างนั้นได้อย่างไร? เรา Google ... แต่ปรากฎว่าคำนั้นไม่ใช่googleableและในบางจุดคุณจะพบกับPDFนี้ ซึ่งแน่นอนว่าเป็นการอ่านที่ยอดเยี่ยม แต่ในกรณีที่คุณลืมคณิตศาสตร์ทั้งหมดที่คุณเรียนรู้ในโรงเรียน (หรือคุณไม่ชอบสัญลักษณ์ทางคณิตศาสตร์เหล่านั้น) มันไร้ประโยชน์เลยทีเดียว

เกิดอะไรขึ้น ไปกันเถอะและ Google อีกแล้ว (อ่าน: 6 ชั่วโมง) และในที่สุดคุณก็พบบทความที่ดีเกี่ยวกับหัวข้อ (รวมถึงรูปภาพที่สวยงาม! ^ _ ^ "):
http://www.planetclegg.com/projects/WarpingTextToSplines.html

ทำรหัสจริง

ในกรณีที่คุณไม่สามารถต่อต้านการดาวน์โหลดไฟล์ PDF แม้ว่าคุณจะสูญเสียความรู้ทางคณิตศาสตร์ไปแล้วเมื่อนานมาแล้ว (และคุณสามารถข้ามลิงค์บทความที่ยอดเยี่ยม ) ตอนนี้คุณอาจคิดว่า: "พระเจ้าสิ่งนี้จะต้องใช้เวลา โค้ดหลายร้อยบรรทัดและ CPU จำนวนมาก "

ไม่มันจะไม่ เพราะเราทำสิ่งที่โปรแกรมเมอร์ทุกคนทำเมื่อมันมาถึงสิ่งคณิตศาสตร์:
เราเพียงแค่โกง

การตั้งค่าพารามิเตอร์ความยาวส่วนโค้ง, วิธีขี้เกียจ

เอาหน้าเราไม่ต้องการความแม่นยำไม่รู้จบในเกมของเราใช่ไหม? ดังนั้นหากคุณไม่ได้ทำงานที่ Nasa และวางแผนที่จะส่งคนไปยังดาวอังคารคุณจะไม่ต้องการ0.000001 pixelโซลูชันที่สมบูรณ์แบบ

ดังนั้นทำอย่างไรเราแผนที่Tบนt? ง่ายและประกอบด้วย 3 ขั้นตอนเท่านั้น:

  1. คำนวณNคะแนนบนเส้นโค้งโดยใช้tและเก็บarc-length(หรือที่รู้จักความยาวของเส้นโค้ง) ที่ตำแหน่งนั้นลงในอาร์เรย์

  2. ในการแมปTลงบนtก่อนอื่นให้คูณTด้วยความยาวทั้งหมดของเส้นโค้งเพื่อหาuและจากนั้นค้นหาอาร์เรย์ของความยาวเพื่อหาดัชนีที่มีค่ามากที่สุดที่เล็กกว่าu

  3. ถ้าเรามีการตีที่แน่นอนกลับค่าอาร์เรย์ที่ดัชนีที่หารด้วยNหากไม่ได้สอดแทรกบิตระหว่างจุดที่เราพบและหน้าหนึ่งแบ่งสิ่งอีกครั้งโดยNและผลตอบแทน

นั่นคือทั้งหมด! ดังนั้นตอนนี้ลองมาดูรหัสที่สมบูรณ์:

function Bezier(a, b, c, d) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;

    this.len = 100;
    this.arcLengths = new Array(this.len + 1);
    this.arcLengths[0] = 0;

    var ox = this.x(0), oy = this.y(0), clen = 0;
    for(var i = 1; i <= this.len; i += 1) {
        var x = this.x(i * 0.05), y = this.y(i * 0.05);
        var dx = ox - x, dy = oy - y;        
        clen += Math.sqrt(dx * dx + dy * dy);
        this.arcLengths[i] = clen;
        ox = x, oy = y;
    }
    this.length = clen;    
}

นี่ต้นโค้งใหม่ของเราและคำนวณarg-lenghtsก็ยังเก็บสุดท้ายของความยาวเป็นtotal lengthโค้งปัจจัยที่สำคัญที่นี่เป็นซึ่งเป็นของเราthis.len Nยิ่งการแมปที่แม่นยำยิ่งขึ้นสำหรับเส้นโค้งของขนาดในภาพด้านบนนั้น100 pointsน่าจะเพียงพอหากคุณต้องการการประมาณความยาวที่ดีบางอย่างที่คล้ายกัน25จะทำงานได้โดยที่เหลือเพียง 1 พิกเซลในของเรา ตัวอย่างเช่น แต่แล้วคุณจะมีการทำแผนที่ที่แม่นยำน้อยลงซึ่งจะส่งผลให้เกิดการกระจายไม่ได้ดังนั้นแม้เมื่อแมปไปTt

Bezier.prototype = {
    map: function(u) {
        var targetLength = u * this.arcLengths[this.len];
        var low = 0, high = this.len, index = 0;
        while (low < high) {
            index = low + (((high - low) / 2) | 0);
            if (this.arcLengths[index] < targetLength) {
                low = index + 1;

            } else {
                high = index;
            }
        }
        if (this.arcLengths[index] > targetLength) {
            index--;
        }

        var lengthBefore = this.arcLengths[index];
        if (lengthBefore === targetLength) {
            return index / this.len;

        } else {
            return (index + (targetLength - lengthBefore) / (this.arcLengths[index + 1] - lengthBefore)) / this.len;
        }
    },

    mx: function (u) {
        return this.x(this.map(u));
    },

    my: function (u) {
        return this.y(this.map(u));
    },

รหัสการแมปจริงก่อนอื่นเราทำการเรียบง่ายbinary searchกับความยาวที่เก็บไว้ของเราเพื่อค้นหาความยาวที่ใหญ่ที่สุดที่เล็กกว่านั้นtargetLengthจากนั้นเราแค่ส่งคืนหรือทำการแก้ไขและกลับมา

    x: function (t) {
        return ((1 - t) * (1 - t) * (1 - t)) * this.a.x
               + 3 * ((1 - t) * (1 - t)) * t * this.b.x
               + 3 * (1 - t) * (t * t) * this.c.x
               + (t * t * t) * this.d.x;
    },

    y: function (t) {
        return ((1 - t) * (1 - t) * (1 - t)) * this.a.y
               + 3 * ((1 - t) * (1 - t)) * t * this.b.y
               + 3 * (1 - t) * (t * t) * this.c.y
               + (t * t * t) * this.d.y;
    }
};

สิ่งนี้คำนวณtบนเส้นโค้งอีกครั้ง

เวลาสำหรับผลลัพธ์

ข้อความแสดงแทน

เมื่อใช้งานmxแล้วmyคุณจะได้Tเส้นโค้งที่เท่ากัน:)

มันไม่ยากใช่ไหม อีกครั้งปรากฎว่าวิธีง่ายๆ (แม้ว่าจะไม่ใช่วิธีที่สมบูรณ์แบบ) จะเพียงพอสำหรับเกม

ในกรณีที่คุณต้องการเห็นรหัสที่สมบูรณ์มี Gist ให้ใช้งาน:
https://gist.github.com/670236

ในที่สุดเร่งเรือ

ดังนั้นสิ่งที่เหลืออยู่ในตอนนี้คือการเร่งเรือไปตามเส้นทางของพวกเขาโดยการทำแผนที่ตำแหน่งTที่เราใช้เพื่อค้นหาtบนโค้งของเรา

ก่อนอื่นเราต้องใช้สมการการเคลื่อนที่สองแบบคือut + 1/2at²และ(v - u) / t

ในรหัสจริงที่มีลักษณะเช่นนี้:

startSpeed = getStartingSpeedInPixels() // Note: pixels
endSpeed = getFinalSpeedInPixels() // Note: pixels
acceleration = (endSpeed - startSpeed) // since we scale to 0...1 we can leave out the division by 1 here
position = 0.5 * acceleration * t * t + startSpeed * t;

จากนั้นเราลดขนาดลง0...1โดยทำ:

maxPosition = 0.5 * acceleration + startSpeed;
newT = 1 / maxPosition * position;

และคุณไปแล้วตอนนี้เรือกำลังเคลื่อนไหวอย่างราบรื่นไปตามเส้นทาง

ในกรณีที่มันไม่ทำงาน ...

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

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

function rotationToMovement(planetSize, rotationSpeed) {
    var r = shipAngle * Math.PI / 180;
    var rr = (shipAngle + rotationSpeed) * Math.PI / 180;
    var orbit = planetSize + shipOrbit;
    var dx = Math.cos(r) * orbit - Math.cos(rr) * orbit;
    var dy = Math.sin(r) * orbit - Math.sin(rr) * orbit;
    return Math.sqrt(dx * dx + dy * dy);
};

และนั่นคือทั้งหมด! ขอบคุณที่อ่าน ;)


7
นี่จะใช้เวลาสักครู่ในการย่อย แต่ว้าวคำตอบที่น่าอัศจรรย์สำหรับคำถามของคุณเอง
AttackHobo

7
ฉันสร้างบัญชีเพื่อถอนคำตอบนี้
ไม่มีใครใน

มีบางจุดที่เพื่อนของฉัน ทำงานเหมือนจับใจ คำถามและคำตอบทั้งคู่โหวตขึ้น
Jace

2
'i' ถูกคูณด้วย 0.05 ในขณะที่ 'len' ถูกตั้งไว้ที่ 100 การทำเช่นนี้ 't' จะถูกแมปเป็น '0-5' แทนที่จะเป็น '0-1'
Evil Activity

1
@EvilActivity ใช่ฉันเห็นด้วยเช่นกันความยาวดั้งเดิมของเขาต้องเท่ากับ 20 แล้วลืมเปลี่ยนเป็น 0.05 ถึง 0.01 ดังนั้นควรมี 'len' แบบไดนามิก (ปรับให้เข้ากับความยาวส่วนโค้งที่แท้จริงหรืออาจเท่ากับหรือเท่ากับ) และคำนวณ "ขั้นตอน" ทีละ 1 / 'len' ฉันพบว่าไม่มีใครแปลกเลยที่นำสิ่งนี้มาหลายปี !!!
Bill Kotsias

4

ปัญหาคือเรือจะไม่ใช้เส้นทางนั้นโดยธรรมชาติ ดังนั้นแม้จะทำงานได้อย่างสมบูรณ์แบบ แต่ก็ยังดูไม่ถูกต้อง

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

คุณเพียงแค่ต้องตั้งค่าคงที่ของคุณ: มวลของ P1, P2, เรือ

ด้วยการติ๊กแต่ละเกม (เวลา: t) คุณกำลังทำ 3 สิ่ง

  1. คำนวณแรงโน้มถ่วงของ p1 บนเรือและ p2 บนเรือเพิ่มเวกเตอร์ผลลัพธ์ลงในเวกเตอร์แรงขับ

  2. คำนวณความเร็วใหม่ของคุณตามการเร่งความเร็วใหม่ของคุณจากขั้นตอนที่ 1

  3. ย้ายเรือตามความเร็วใหม่ของคุณ

ดูเหมือนว่าจะมีงานมาก แต่ก็สามารถทำได้ในรหัสโหลและจะดูเป็นธรรมชาติมาก

หากคุณต้องการความช่วยเหลือเกี่ยวกับฟิสิกส์แจ้งให้เราทราบ


ฉันอาจจะพิจารณาการทดสอบว่าถ้าคุณสามารถให้วิธีการทำเช่นนี้ภายในฟังก์ชั่นที่ใช้t:)
Ivo Wetzel

- แต่ในการเขียนโปรแกรมเกมคุณไม่ได้ใช้ t เป็นตัวแปร โดยทั่วไปคุณอยู่ในสถานการณ์ที่แปรผันเพราะคุณเพียงแค่คำนวณ dx และ dy ใหม่สำหรับเรือ นี่คือตัวอย่างของการโคจรสองดาวเคราะห์ (ใน Flash) aharrisbooks.net/flash/fg2r12/twoPlanets.html - และนี่ก็เป็นสิ่งเดียวกันใน Python: aharrisbooks.net/pythonGame/ch09/twoPlanets.py
Two pi

2

ฉันพบบทความที่ยอดเยี่ยมที่อธิบายถึงวิธีแก้ปัญหาที่เป็นไปได้สำหรับปัญหานี้ด้วยตัวอย่างโค้ดที่เขียนด้วยจาวาสคริปต์ มันทำงานโดย "สะกิดค่า t" ในทิศทางที่ถูกต้อง

แต่เราสามารถใช้ความจริงที่ว่าความยาวขาเฉลี่ย d_avg สำหรับการแจกแจงจุดใด ๆ นั้นเกือบจะเหมือนกับความยาวของขาที่จุดที่เว้นระยะเท่ากันจะสร้างขึ้น (ความคล้ายคลึงกันนี้เพิ่มขึ้นเมื่อ n เพิ่มขึ้น) หากเราคำนวณความแตกต่าง d_err ระหว่างความยาวขาจริง d และความยาวขาเฉลี่ย d_avg ดังนั้นพารามิเตอร์เวลา t ที่สอดคล้องกับแต่ละจุดสามารถถูกสะกิดเพื่อลดความแตกต่างนี้

คำถามนี้มีคำตอบที่ยอดเยี่ยมอยู่แล้ว แต่ฉันพบว่าวิธีนี้ควรค่าแก่การสังเกต


1

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

ไม่รู้ว่ามันสมเหตุสมผลหรือไม่ แต่นี่ช่วยฉันได้อย่างแน่นอน


0

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

ทำโมเดลเรือด้วยพลังผลักดัน

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

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

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


ปัญหาคือนี่คือเห็บทั้งหมดตามไม่มีตำแหน่งกลาง มันเป็นเกมที่เล่นได้หลายคนในเครือข่ายและการส่งตำแหน่งทั้งหมด 600+ ลำในเกมเต็มจะฆ่าทุกเครือข่าย มีเพียงเหตุการณ์ที่ส่ง tickOffset เท่านั้นส่วนที่เหลือจะคำนวณตาม tick โลกปัจจุบันและออฟเซ็ต
Ivo Wetzel

ฉันแก้ไขคำตอบของฉัน
AttackHobo

0

หากฉันเข้าใจถูกต้องปัญหาของคุณจะถูก จำกัด มากเกินไป

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

คุณจะต้องผ่อนคลายปัญหาเล็กน้อยเพื่อให้แก้ปัญหาได้


2
ถ้าอย่างนั้นจะผ่อนคลายมันได้อย่างไร? สิ่งที่ฉันสามารถจินตนาการได้คือการปรับเปลี่ยน T ที่ฉันเสียบเข้ากับสิ่งที่เส้นทาง bezier ฉันจะต้องปรับขนาดมันก่อนเพื่อให้เติบโตช้าลงถึง 0.5 และเร็วขึ้นเป็น 1 ดังนั้นเรือจะชะลอตัวลงจากความเร็วเดิมไปเป็นค่าคงที่ที่กึ่งกลางของเส้นโค้งแล้วเร่งอีกครั้งจากความเร็วนี้ไปจนถึงความเร็วที่สิ้นสุด ของเส้นโค้ง
Ivo Wetzel

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

ยังคงฉันติดอยู่กับวิธีการเสียบการเร่งความเร็วในทุกสิ่งฉันต้องแก้ไข T อย่างใด: /
Ivo Wetzel

0

ฉันเจอคำตอบนี้เพราะฉันต้องการกระจายคะแนนอย่างสม่ำเสมอตามเส้นทาง svg ที่ใช้เส้นโค้งเบซิเยร์

แม้ว่า MDN จะบอกว่าเลิกใช้แล้ว แต่คุณสามารถใช้path.getPointAtLengthเพื่อให้ได้ผลลัพธ์ที่ถูกต้อง https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLength

ขณะนี้ใช้งานได้ใน Chrome / Safari / Firefox และควรทำงานใน IE / Edge เช่นกัน แต่ฉันไม่ได้ตรวจสอบ 2 เหล่านั้น


-1

ปัญหาเกี่ยวกับวิธีแก้ไขปัญหาที่ยอมรับได้

เนื่องจาก Bezier เป็นฟังก์ชันเลขชี้กำลังเราคาดหวังอัตราล่วงหน้าที่แตกต่างกันในส่วนต่าง ๆ ของเส้นโค้ง

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

ตอบโต้อคติผ่านการสุ่มตัวอย่างที่มีแนวทาง

วิธีการแก้ปัญหาทางเลือกคือการเส้นตรงแมปdistanceไปtหลังจากโต้ตอบอคติธรรมชาติที่ฟังก์ชั่น Bezier ผลิต

สมมติว่านี่คือสิ่งที่เราต้องการ:

curve length = 10

t      distance
0.2    2
0.4    4
0.6    6
0.8    8
1.0    10

แต่นี่คือสิ่งที่เราได้รับจากฟังก์ชันตำแหน่ง Bezier:

t      distance
0.2    0.12
0.4    1.22
0.6    2.45
0.8    5.81
1.0    10.00

โดยการดูNตัวอย่างที่ถ่ายเราจะเห็นว่า deltas ระยะทางมากที่สุดและ resample ("แยก") ตรงกลางระหว่างสองระยะทางติดกันเพิ่มขึ้นN1 ตัวอย่างเช่นแยกที่t=0.9(ซึ่งอยู่กึ่งกลางในเดลต้าที่ใหญ่ที่สุด) เราอาจ จะได้รับ:

0.8    5.81
0.9    7.39
1.0    10.00

เราทำซ้ำขั้นตอนนี้สำหรับช่วงระยะที่ใหญ่ที่สุดต่อไปจนกว่าเดลต้าสูงสุดระหว่างสองระยะทางในการตั้งค่าทั้งหมดจะอยู่ด้านล่างบางส่วนminDistanceDeltaและอื่น ๆ โดยเฉพาะอย่างน้อยกว่าepsilonออกไปจากระยะทางที่เฉพาะเจาะจงเราต้องการที่จะแมปไปตามขั้นตอนของt; จากนั้นเราสามารถแมปตามtขั้นตอนที่เราต้องการเป็นเส้นตรงกับdistanceเอส สิ่งนี้จะสร้าง hashtable / map ซึ่งคุณสามารถเข้าถึงได้อย่างถูกและมีค่าที่คุณสามารถอ่านได้ระหว่างที่รันไทม์โดยไม่มีอคติ

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

เราจบลงด้วยสิ่งที่เราต้องการ:

epsilon: 0.01

t            distance
0.200417     2.00417
0.3998132    3.9998132
0.600703     6.00703
0.800001     8.00001
0.9995309    9.995309

เนื่องจากเรามีการคาดเดาในทุกขั้นตอนเราจะไม่ได้รับที่แน่นอนในระยะทางที่แน่นอน2, 4ฯลฯ เราต้องการ แต่ผ่านซ้ำซ้ำเหล่านี้ได้รับใกล้พอที่จะค่าระยะทางที่ต้องการเพื่อให้คุณสามารถแมปที่คุณtทำตามขั้นตอนด้วยความถูกต้องเป็นธรรม, การขจัดอคติเนื่องจาก เพื่อการสุ่มตัวอย่างที่ใกล้เคียงกัน

จากนั้นคุณสามารถดึงข้อมูลเช่นt=0.5ตามที่ Ivo ทำในคำตอบของเขาเช่นโดยการแทรกระหว่างค่าที่ใกล้เคียงที่สุดสองค่าด้านบน ( 3.9998132และ6.00703)

ข้อสรุป

สำหรับกรณีส่วนใหญ่แก้ปัญหาเปาจะทำงานได้ดี แต่สำหรับกรณีที่มีอคติจะต้องหลีกเลี่ยงค่าใช้จ่ายทั้งหมดให้แน่ใจว่าคุณกำลังจะกระจายอย่างสม่ำเสมอเท่าที่เป็นไปได้และเป็นเส้นตรงแล้วแมปไปdistancet

หมายเหตุ: แยกที่สามารถทำได้มากกว่า stochastically แยกลงตรงกลางแต่ละครั้งตัวอย่างเช่นเราอาจจะแยกออกว่าช่วงเวลาตัวอย่างแรกที่มากกว่าt=0.827t=0.9

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.