2008-09-17 21 views
21

Jeśli główna oś elipsy jest pionowa lub pozioma, łatwo obliczyć obwiednię, ale co się stanie, gdy elipsa zostanie obrócona?Jak obliczyć wyrównane do osi pole ograniczające elipsy?

Jedyny sposób, jaki mogę sobie wyobrazić to obliczenie wszystkich punktów na obwodzie i znalezienie wartości max/min xiy. Wygląda na to, że powinien istnieć prostszy sposób.

Jeśli istnieje funkcja (w sensie matematycznym), która opisuje elipsę pod dowolnym kątem, to mógłbym użyć jej pochodnej do znalezienia punktów, w których nachylenie jest zerowe lub niezdefiniowane, ale nie mogę go znaleźć.

Edycja: aby wyjaśnić, potrzebuję wyrównanego do osi prostokąta ograniczającego, tj. Nie powinien on być obracany elipsą, ale pozostaje wyrównany względem osi x, więc transformacja obwiedni nie będzie działać.

Odpowiedz

31

Można spróbować za pomocą sparametryzowanych równania elipsy obracać pod dowolnym kątem:

x = h + a*cos(t)*cos(phi) - b*sin(t)*sin(phi) [1] 
y = k + b*sin(t)*cos(phi) + a*cos(t)*sin(phi) [2] 

... gdzie elipsa ma środkową (h, k) oś półprzewodnikową a i oś semibleor b, i jest obracana o kąt phi.

Następnie można różnicować i rozwiązania dla gradientu = 0:

0 = dx/dt = -a*sin(t)*cos(phi) - b*cos(t)*sin(phi) 

=>

tan(t) = -b*tan(phi)/a [3] 

Które powinny dać ci wiele rozwiązań dla T (z których dwa są Państwo zainteresowani), wtyczką z powrotem do [1], aby uzyskać maksimum i min x.

powtarzania na [2]:

0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi) 

=>

tan(t) = b*cot(phi)/a [4] 

Umożliwia próby przykład:

Rozważmy elipsę w punkcie (0,0), z a = 2 , b = 1, obrócone o PI/4:

[1] =>

x = 2*cos(t)*cos(PI/4) - sin(t)*sin(PI/4) 

[3] =>

tan(t) = -tan(PI/4)/2 = -1/2 

=>

t = -0.4636 + n*PI 

Jesteśmy zainteresowani t = -0,4636 i t = -3,6052

więc otrzymujemy:

x = 2*cos(-0.4636)*cos(PI/4) - sin(-0.4636)*sin(PI/4) = 1.5811 

i

x = 2*cos(-3.6052)*cos(PI/4) - sin(-3.6052)*sin(PI/4) = -1.5811 
+0

Dzięki. To działa, z wyjątkiem tego, że masz literówkę w równaniu drugim. Znak minus powinien być plusem. –

+0

Naprawiono, wydaje mi się, że podążyłem za rozwiązaniem dla tan (t) na [2] również, więc to naprawiłem. Mam nadzieję, że zauważyłeś wszystkie moje błędy - wszystko jest zapisane na kopercie tutaj ..;) –

+0

Myślę, że jest inny błąd, w przykładzie: pierwsza wartość t dla x wynosi -0.4636, nie powinna być druga -3.6052 (wynosi -0,4636 - pi)? – brianmearns

2

Myślę, że najbardziej użyteczną formułą jest ta. Wielokropek obracany z kąt phi od pochodzenia jak w przypadku równania:

alt text

alt text

gdzie (H, K), jest środkiem, a i b wielkości osi głównych i pomocniczych oraz t zmienia się od -pi do pi.

tym, powinieneś być w stanie określić, dla których t dx/dt lub dy/dt idzie do 0.

+0

Czuję się teraz tak powolny, wziąłem mnie wieku, aby napisać odpowiedź na górę T.T –

5

To jest względne proste, ale trochę trudne do wytłumaczenia, ponieważ nie dały nam sposób reprezentować swój elipsę. Jest tak wiele sposobów na zrobienie tego ..

W każdym razie ogólna zasada brzmi następująco: Nie można obliczyć wyrównanej osi ramki granicznej bezpośrednio. Można jednak obliczyć ekstrema elipsy wx i y jako punkty w przestrzeni 2D.

W tym celu wystarczy przyjąć równanie x (t) = ellipse_equation (t) i y (t) = ellipse_equation (t). Uzyskaj pochodną pierwszego rzędu i rozwiąż go, ponieważ jest to root. Ponieważ mamy do czynienia z elipsami opartymi na trygonometrii, które są proste. Powinieneś skończyć z równaniem, które albo dostaje korzenie przez atan, acos lub asin.

Wskazówka: Aby sprawdzić kod, spróbuj go z nieobjętą elipsą: Powinieneś mieć korzenie 0, Pi/2, Pi i 3 * Pi/2.

Zrób to dla każdej osi (x i y). Otrzymasz co najwyżej cztery korzenie (mniej, jeśli twoja elipsa jest zdegenerowana, np. Jeden z promieni jest zerowy). Ewaluuj pozycje u nasady i uzyskaj wszystkie skrajne punkty elipsy.

Teraz już prawie jesteś. Zdobycie ramki granicznej elipsy jest tak proste jak skanowanie tych czterech punktów dla xmin, xmax, ymin i ymax.

Btw - jeśli masz problemy ze znalezieniem równania swojej elipsy: spróbuj zmniejszyć to do przypadku, w którym masz elipsę ustawioną w osi z centrum, dwoma promieniami i kątem obrotu wokół środka.

Jeśli tak stać równania:

// the ellipse unrotated: 
    temp_x (t) = radius.x * cos(t); 
    temp_y (t) = radius.y = sin(t); 

    // the ellipse with rotation applied: 
    x(t) = temp_x(t) * cos(angle) - temp_y(t) * sin(angle) + center.x; 
    y(t) = temp_x(t) * sin(angle) + temp_y(t) * cos(angle) + center.y; 
4

znalazłem prosty wzór na http://www.iquilezles.org/www/articles/ellipses/ellipses.htm (i zignorował oś Z).

I wdrożone go mniej więcej tak:

num ux = ellipse.r1 * cos(ellipse.phi); 
num uy = ellipse.r1 * sin(ellipse.phi); 
num vx = ellipse.r2 * cos(ellipse.phi+PI/2); 
num vy = ellipse.r2 * sin(ellipse.phi+PI/2); 

num bbox_halfwidth = sqrt(ux*ux + vx*vx); 
num bbox_halfheight = sqrt(uy*uy + vy*vy); 

Point bbox_ul_corner = new Point(ellipse.center.x - bbox_halfwidth, 
           ellipse.center.y - bbox_halfheight); 

Point bbox_br_corner = new Point(ellipse.center.x + bbox_halfwidth, 
           ellipse.center.y + bbox_halfheight); 
+0

proste i ładne rozwiązanie, dzięki! –

+0

Implementacja JavaScriptu https://jsfiddle.net/Kolosovskiy/sLc7ynd1/5/ –

0

Kod ten jest oparty na user1789690 kodu przyczyniły się wyżej, ale realizowane w Delphi. Przetestowałem to i o ile mogę powiedzieć, że działa idealnie. Spędziłem cały dzień szukając algorytmu lub jakiegoś kodu, przetestowałem niektóre, które nie działały, i byłem bardzo szczęśliwy, że w końcu znalazłem powyższy kod. Mam nadzieję, że ktoś uzna to za przydatne. Ten kod obliczy obwiednię obróconej elipsy. Ramka ograniczająca jest wyrównana względem osi i NIE obraca się razem z elipsą. Promienie odnoszą się do elipsy przed jej obróceniem.

type 

    TSingleRect = record 
    X:  Single; 
    Y:  Single; 
    Width: Single; 
    Height: Single; 
    end; 

function GetBoundingBoxForRotatedEllipse(EllipseCenterX, EllipseCenterY, EllipseRadiusX, EllipseRadiusY, EllipseAngle: Single): TSingleRect; 
var 
    a: Single; 
    b: Single; 
    c: Single; 
    d: Single; 
begin 
    a := EllipseRadiusX * Cos(EllipseAngle); 
    b := EllipseRadiusY * Sin(EllipseAngle); 
    c := EllipseRadiusX * Sin(EllipseAngle); 
    d := EllipseRadiusY * Cos(EllipseAngle); 
    Result.Width := Hypot(a, b) * 2; 
    Result.Height := Hypot(c, d) * 2; 
    Result.X  := EllipseCenterX - Result.Width * 0.5; 
    Result.Y  := EllipseCenterY - Result.Height * 0.5; 
end; 
1

Oto wzór dla przypadku, gdy elipsy jest przez jego ognisk i mimośród (w przypadku, gdy jest podany z odcinków osiowych, środkowy i kątem, patrz np. G. Odpowiedź za user1789690).

Mianowicie, jeżeli ogniska komórek (X0, Y0) i (x1, y1) i mimośród jest e, a

bbox_halfwidth = sqrt(k2*dx2 + (k2-1)*dy2)/2 
bbox_halfheight = sqrt((k2-1)*dx2 + k2*dy2)/2 

gdzie

dx = x1-x0 
dy = y1-y0 
dx2 = dx*dx 
dy2 = dy*dy 
k2 = 1.0/(e*e) 

I pochodzi formuł z odpowiedzi autor: user1789690 i Johan Nilsson.

1

Brilian Johan Nilsson. Mam transkrypcji kodu C# - ellipseAngle są teraz w stopniach:

private static RectangleF EllipseBoundingBox(int ellipseCenterX, int ellipseCenterY, int ellipseRadiusX, int ellipseRadiusY, double ellipseAngle) 
{ 
    double angle = ellipseAngle * Math.PI/180; 
    double a = ellipseRadiusX * Math.Cos(angle); 
    double b = ellipseRadiusY * Math.Sin(angle); 
    double c = ellipseRadiusX * Math.Sin(angle); 
    double d = ellipseRadiusY * Math.Cos(angle); 
    double width = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2)) * 2; 
    double height = Math.Sqrt(Math.Pow(c, 2) + Math.Pow(d, 2)) * 2; 
    var x= ellipseCenterX - width * 0.5; 
    var y= ellipseCenterY + height * 0.5; 
    return new Rectangle((int)x, (int)y, (int)width, (int)height); 
} 
0

To jest moja funkcja znajdowania ciasno prostokąt elipsy z dowolną orientacją

mam OpenCV rect i punkt dla realizacji:

CG - ośrodek elipsy

wielkość - duże, mała oś elipsy

kąt - orientacja elipsy

cv::Rect ellipse_bounding_box(const cv::Point2f &cg, const cv::Size2f &size, const float angle) { 

    float a = size.width/2; 
    float b = size.height/2; 
    cv::Point pts[4]; 

    float phi = angle * (CV_PI/180); 
    float tan_angle = tan(phi); 
    float t = atan((-b*tan_angle)/a); 
    float x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi); 
    float y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi); 
    pts[0] = cv::Point(cvRound(x), cvRound(y)); 

    t = atan((b*(1/tan(phi)))/a); 
    x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi); 
    y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi); 
    pts[1] = cv::Point(cvRound(x), cvRound(y)); 

    phi += CV_PI; 
    tan_angle = tan(phi); 
    t = atan((-b*tan_angle)/a); 
    x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi); 
    y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi); 
    pts[2] = cv::Point(cvRound(x), cvRound(y)); 

    t = atan((b*(1/tan(phi)))/a); 
    x = cg.x + a*cos(t)*cos(phi) - b*sin(t)*sin(phi); 
    y = cg.y + b*sin(t)*cos(phi) + a*cos(t)*sin(phi); 
    pts[3] = cv::Point(cvRound(x), cvRound(y)); 

    long left = 0xfffffff, top = 0xfffffff, right = 0, bottom = 0; 
    for (int i = 0; i < 4; i++) { 
     left = left < pts[i].x ? left : pts[i].x; 
     top = top < pts[i].y ? top : pts[i].y; 
     right = right > pts[i].x ? right : pts[i].x; 
     bottom = bottom > pts[i].y ? bottom : pts[i].y; 
    } 
    cv::Rect fit_rect(left, top, (right - left) + 1, (bottom - top) + 1); 
    return fit_rect; 
} 
1

Jeśli pracujesz z OpenCV/C++ i używać cv::fitEllipse(..) funkcji, może być konieczne obwiedni rect elipsy. Tutaj zrobiłem rozwiązanie, używając odpowiedzi Mike'a:

// tau = 2 * pi, see tau manifest 
const double TAU = 2 * std::acos(-1); 

cv::Rect calcEllipseBoundingBox(const cv::RotatedRect &anEllipse) 
{ 
    if (std::fmod(std::abs(anEllipse.angle), 90.0) <= 0.01) { 
     return anEllipse.boundingRect(); 
    } 

    double phi = anEllipse.angle * TAU/360; 
    double major = anEllipse.size.width/2.0; 
    double minor = anEllipse.size.height/2.0; 

    if (minor > major) { 
     std::swap(minor, major); 
     phi += TAU/4; 
    } 

    double cosPhi = std::cos(phi), sinPhi = std::sin(phi); 
    double tanPhi = sinPhi/cosPhi; 

    double tx = std::atan(-minor * tanPhi/major); 
    cv::Vec2d eqx{ major * cosPhi, - minor * sinPhi }; 
    double x1 = eqx.dot({ std::cos(tx),   std::sin(tx)   }); 
    double x2 = eqx.dot({ std::cos(tx + TAU/2), std::sin(tx + TAU/2) }); 

    double ty = std::atan(minor/(major * tanPhi)); 
    cv::Vec2d eqy{ major * sinPhi, minor * cosPhi }; 
    double y1 = eqy.dot({ std::cos(ty),   std::sin(ty)   }); 
    double y2 = eqy.dot({ std::cos(ty + TAU/2), std::sin(ty + TAU/2) }); 

    cv::Rect_<float> bb{ 
     cv::Point2f(std::min(x1, x2), std::min(y1, y2)), 
     cv::Point2f(std::max(x1, x2), std::max(y1, y2)) 
    }; 

    return bb + anEllipse.center; 
}