Một thuật toán đơn giản để tính các điểm phân bố đồng đều trên một hình elip là gì?


8

Tôi đang tìm kiếm một thuật toán đơn giản để vẽ các điểm phân bố đồng đều trên một hình elip, với các trục chính và trục nhỏ. Điều này thực sự dễ thực hiện với một vòng tròn như vậy:

var numberOfPoints = 8;
var angleIncrement = 360 / numberOfPoints;
var circleRadius = 100;
for (var i = 0; i < numberOfPoints; i++) {
    var p = new Point();
    p.x = (circleRadius * Math.cos((angleIncrement * i) * (Math.PI / 180)));
    p.y = (circleRadius * Math.sin((angleIncrement * i) * (Math.PI / 180)));
}

(tạo ra các điểm có cùng khoảng cách với các điểm lân cận của chúng) nhưng tôi không thể tìm hoặc tìm ra cách để làm điều này trên hình elip. (Giải pháp trong AS3 được ưa thích, nhưng không bắt buộc.)


3
Chỉ cần rõ ràng, bạn muốn các điểm được cách đều nhau theo chu vi?
tenpn

Câu trả lời:


6

Tham số lại nó theo chiều dài cung và mẫu đồng đều. Nếu bạn cần trợ giúp làm toán, tôi sẽ hỏi nó ở đây:
https://math.stackexchange.com/

nó cũng được hỏi ở đây: /mathpro/28070/finding-n-point-that-are-equidistant-around-the-circumference-of-an-ellipse


Wow điều này phức tạp hơn tôi nghĩ! Vì vậy, có, về cơ bản, tôi đang cố gắng thực hiện điều này thoátordie.com/goodEllipse.gif (lấy từ chủ đề này bigresource.com/Tracker/Track-flash-DO1WzX6KNq ). Nếu tôi hiểu điều này một cách chính xác, điều đó có nghĩa là tôi không muốn làm cho các điểm tương đương nhau theo chiều dài cung.
Justin C. Vòng

Tôi nghĩ rằng bạn muốn vẽ các điểm tương đương theo chiều dài cung. Không có một công thức tốt đẹp nào cho chu vi của hình elip. Trên trang mathoverflow có một liên kết đến trang này en.wikipedia.org/wiki/Circumference cung cấp các xấp xỉ cho chu vi mà bạn sẽ có thể sử dụng.
Jonathan Fischoff

Bài học thực sự tôi nghĩ là nếu bạn muốn toán học nói chung đơn giản, hãy sử dụng đa thức hoặc các hàm hữu tỷ. Dấu chấm lửng có vẻ đơn giản, nhưng có các thuộc tính phức tạp khiến chúng khó tính toán. Đa thức là các đường cong bahave rất tốt và gần đúng (như elip) rất tốt.
Jonathan Fischoff

6

Xấp xỉ hợp lý

Như đã nêu trong các câu trả lời khác, không có cách chính xác để làm điều này. Tuy nhiên, có thể ước tính một cách hiệu quả một giải pháp.

Công thức của tôi sẽ chỉ xử lý góc phần tư phía trên bên phải . Thay đổi dấu hiệu khác nhau sẽ cần phải được áp dụng để xử lý các góc phần tư khác.

Đặt d là khoảng cách vòng cung mong muốn của bạn giữa các điểm liên tiếp. Giả sử điểm được vẽ cuối cùng là tại (x, y) .

  |
b +-------._  (x,y)
  |         `@-._
  |              `-.
  |                 `.
  |                   \
 -+--------------------+--->
 O|                    a

Sau đó, điểm tiếp theo sẽ được vẽ ở các tọa độ sau:

x' = x + d / sqrt(1 + b²x² / (a²(a²-x²)))
y' = b sqrt(1 - x'²/a²)

Bằng chứng

Đặt điểm tiếp theo là (x + x, y + y) . Cả hai điểm đều thỏa mãn phương trình elip:

x²/a² + y²/b² = 1
(xx)²/a² + (yy)²/b² = 1

Loại bỏ y trong các phương trình cho:

Δy = b (sqrt(1 - (xx)²/a²) - sqrt(1 - x²/a²))

Chúng tôi giả sử Δx là đủ nhỏ, vì vậy chúng tôi thay thế f (x + x) -f (x) bằng f '(x) x bằng cách sử dụng xấp xỉ tuyến tính cho f' :

Δy = -bxΔx / (a² sqrt(1 - x²/a²))

Nếu d đủ nhỏ, thì ΔxΔy đủ nhỏ và độ dài cung gần với khoảng cách eidianidian giữa các điểm. Do đó, xấp xỉ sau là hợp lệ:

Δx² + Δy² ~ d²

Chúng tôi thay thế Δy ở trên và giải quyết cho Δx :

Δx ~ d / sqrt(1 + b²x² / (a²(a²-x²)))

Nếu d không đủ nhỏ thì sao?

Nếu d là quá lớn đối với các xấp xỉ trên để có giá trị, chỉ cần thay thế d với d / N , ví dụ N = 3 , và chỉ vẽ một điểm trong tổng số N .

Lưu ý cuối cùng

Phương pháp này có vấn đề ở extrema ( x = 0 hoặc y = 0 ), có thể được xử lý bằng cách sử dụng các xấp xỉ bổ sung ( nghĩa là bỏ qua điểm cuối của góc phần tư, cho dù nó có thực sự được vẽ hay không).

Xử lý toàn bộ hình elip có thể sẽ mạnh mẽ hơn bằng cách làm lại toàn bộ vật bằng cách sử dụng tọa độ cực. Tuy nhiên, đó là một số công việc và đây là một câu hỏi cũ, vì vậy tôi sẽ chỉ làm điều đó nếu có sự quan tâm từ người đăng ban đầu :-)


1
Tôi tự động upvote cho nghệ thuật ASCII.
Jimmy

0

Tôi loại phụ thuộc chính xác những gì bạn có nghĩa là "đồng đều". Tôi đã viết một bài về việc sử dụng dấu chấm lửng trong trò chơi của mình tại đây: http://world-create.blogspot.com/2009/01/ellipse-maths.html

Từ bài viết:

class Ellipse
{
  Vector3 m_centre;
  Vector3 m_up;
  Vector3 m_along;
  float m_h;
  float m_l;
};

Vector3 Ellipse::PointAt(float t)
{
  float c = cos(t);
  float s = sin(t);

  return m_h * c * m_up + m_l * s * m_along + m_centre;      
}

Bạn có thể nhận được các điểm cách đều nhau xung quanh hình elip theo góc bằng cách thực hiện:

PointAt(0.0f);
PointAt(kHalfPi);
PointAt(kPi);
PointAt(kHalfPi * 3.0f);
PointAt(kTwoPi);

Nhưng tùy thuộc vào chi tiết cụ thể của hình elip của bạn, các giá trị này có thể được gộp lại (nếu có một kết thúc "nhọn" cho hình elip).


0

Câu trả lời, với mã Java đầy đủ nằm trên StackOverflow tại đây

Đã trả lời bởi:

chỉnh sửa ngày 11 tháng 12 năm 13 lúc 4:14 John Paul

đã trả lời ngày 11 tháng 12 năm 13 lúc 3:48 Dave

package com.math;

public class CalculatePoints {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    /*
     * 
    dp(t) = sqrt( (r1*sin(t))^2 + (r2*cos(t))^2)
    circ = sum(dp(t), t=0..2*Pi step 0.0001)

    n = 20

    nextPoint = 0
    run = 0.0
    for t=0..2*Pi step 0.0001
        if n*run/circ >= nextPoint then
            set point (r1*cos(t), r2*sin(t))
            nextPoint = nextPoint + 1
        next
        run = run + dp(t)
    next
 */


    double r1 = 20.0;
    double r2 = 10.0;

    double theta = 0.0;
    double twoPi = Math.PI*2.0;
    double deltaTheta = 0.0001;
    double numIntegrals = Math.round(twoPi/deltaTheta);
    double circ=0.0;
    double dpt=0.0;

    /* integrate over the elipse to get the circumference */
    for( int i=0; i < numIntegrals; i++ ) {
        theta += i*deltaTheta;
        dpt = computeDpt( r1, r2, theta);
        circ += dpt;
    }
    System.out.println( "circumference = " + circ );

    int n=20;
    int nextPoint = 0;
    double run = 0.0;
    theta = 0.0;

    for( int i=0; i < numIntegrals; i++ ) {
        theta += deltaTheta;
        double subIntegral = n*run/circ;
        if( (int) subIntegral >= nextPoint ) {
            double x = r1 * Math.cos(theta);
            double y = r2 * Math.sin(theta);
            System.out.println( "x=" + Math.round(x) + ", y=" + Math.round(y));
            nextPoint++;
        }
        run += computeDpt(r1, r2, theta);
    }
}

static double computeDpt( double r1, double r2, double theta ) {
    double dp=0.0;

    double dpt_sin = Math.pow(r1*Math.sin(theta), 2.0);
    double dpt_cos = Math.pow( r2*Math.cos(theta), 2.0);
    dp = Math.sqrt(dpt_sin + dpt_cos);

    return dp;
}

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.