Нахождение точки внутри сектора

РЕШЕНО: см. мой ответ.

Я пытаюсь найти точку, которая находится внутри дуги, поэтому, когда происходит заливка, она случайно не заполняет область за пределами дуги. Пока абсолютное значение расстояния между двумя углами: начало и конец; меньше, чем PI, это работает (есть пара крайних случаев, когда нарисованные линии настолько близки, что выбранная точка является частью этих линий, но это для другого дня...).

Проблема, с которой я сталкиваюсь, заключается в том, что когда абсолютное значение расстояния между начальным и конечным углами больше, чем PI, затопление происходит на внешней стороне дуги, а не на внутренней. Один из многих примеров: если дуга начинается в 0 и заканчивается в 3PI/2, абсолютное значение расстояния равно 3PI/2, заполнение происходит между углами, как если бы абсолютное значение расстояния было PI/2, и заполняет весь экран. кроме дуги в форме pacman.

РЕДАКТИРОВАТЬ:

Чтобы не было путаницы, вот определение дуги по аллегро (и вообще по тригонометрии):

void arc(BITMAP *bmp, int x, y, фиксированный ang1, ang2, int r, int color);

Рисует дугу окружности [минус начальная/конечная стороны или центральная точка] с центром [так в оригинале] x, y и радиусом r в направлении против часовой стрелки [так в оригинале], начиная с угла a1 и заканчивая, когда он достигает a2....Ноль находится справа от центральной [так в оригинале] точки, и большие значения вращаются оттуда против часовой стрелки [так в оригинале].

Квадратные скобки - это мое обозначение.

Я уже позаботился о переходе от аллегро (глупого) использования fixed integers к правильным значениям radian.

КОНЕЦ РЕДАКТИРОВАТЬ

void Arc::Draw(BITMAP* dest, int color, bool filled, bool showCenter, bool showSides) {

    if(showSides || filled) {
        Line initial(GetX(), GetY(), GetZ(), GetStartPoint().GetX(), GetStartPoint().GetY(), GetZ(), false);
        initial.SetColor(color);

        Line terminal(GetX(), GetY(), GetZ(), GetEndPoint().GetX(), GetEndPoint().GetY(), GetZ(), false);
        terminal.SetColor(color);

        initial.Draw(dest, initial.GetColor(), false);
        terminal.Draw(dest, terminal.GetColor(), false);

    } else if(showCenter) {
        putpixel(dest, GetX(), GetY(), color);
    }

    //Draw arc first to prevent flood overflow.
    arc(dest, GetX(), GetY(), AngleConverter::RadianToFixed(_startAngle), AngleConverter::RadianToFixed(_endAngle), _radius, color);

    if(filled) {

        double distance = std::fabs(this->_endAngle - this->_startAngle);
        if(distance < a2de::A2DE_PI) {

            Line displace(GetStartPoint(), GetEndPoint(), false);
            Point displacePoint(displace.GetCenter());
            floodfill(dest, displacePoint.GetX(), displacePoint.GetY(), color);

        } else if(distance > a2de::A2DE_PI) {

            Line displace(GetStartPoint(), GetEndPoint(), false);
            Vector2D center_of_displacement(displace.GetCenter());
            Vector2D center_point(this->_center);
            Vector2D direction_of_center(center_of_displacement - center_point);

            double angle = std::atan2(direction_of_center.GetY(), direction_of_center.GetX());
            Vector2D flood_point = center_point - direction_of_center;
            flood_point += angle;

            double x = flood_point.GetX() > 0.0 ? std::ceilf(flood_point.GetX()) : std::floorf(flood_point.GetX());
            double y = flood_point.GetY() > 0.0 ? std::ceilf(flood_point.GetY()) : std::floorf(flood_point.GetY());
            floodfill(dest, x, y, color);

        } else {

            if(_startAngle == 0.0 || _endAngle == a2de::A2DE_2PI) {
                floodfill(dest, GetX(), GetY() - 1, color);
            } else if(_endAngle == 0.0 || _startAngle == a2de::A2DE_PI) {
                floodfill(dest, GetX(), GetY() + 1, color);
            }

        }

    }
}

person Casey    schedule 10.07.2012    source источник
comment
если у вас есть только угол / начальный угол, вы не можете определить дугу, у вас должен быть 1) угол поворота со знаком или направление по часовой стрелке / против часовой стрелки, которое говорит, какая часть дуги, идущей от начала до конца, должна быть рассмотрена   -  person Felice Pollano    schedule 10.07.2012
comment
@FelicePollano Направление по часовой стрелке/против часовой стрелки автоматически определяется графической библиотекой. Если начальный угол больше конечного угла, то это по часовой стрелке и наоборот.   -  person Casey    schedule 10.07.2012
comment
Кажется, вы используете термин дуга для того, что обычно называют сектором.   -  person n. 1.8e9-where's-my-share m.    schedule 10.07.2012
comment
@н.м. Официально аллегро рисует только дугу (изогнутую границу сектора). Я расширяю его, позволяя пользователю также рисовать стороны сектора, длину которого он граничит. См. дугу.   -  person Casey    schedule 10.07.2012
comment
В документации прямо сказано, что направление направлено против часовой стрелки. Это категорически противоречит вашему заявлению о направлении, которое я считаю (а) ложным и (б) причиной ваших затруднений. Вы должны говорить угловые расстояния со знаком (против часовой стрелки - положительно, по часовой стрелке - отрицательно). Никогда не принимайте их абсолютные значения, в этом нет смысла.   -  person n. 1.8e9-where's-my-share m.    schedule 10.07.2012
comment
@н.м. Правильно, но: код должен определять, больше ли длина дуги половины окружности, является ли дуга от 0 до 3PI/2 или от 3PI/2 до 0 (или любые бесконечные комбинации, которые приводят к длине больше, чем PI) общая длина одинакова и никогда не может быть отрицательной, поэтому требуется абсолютное значение.   -  person Casey    schedule 10.07.2012
comment
Вам ничего из этого не нужно. Зафиксируйте направление против часовой стрелки, как и все в известной Вселенной, и вы будете рады, что сделали это. Общая длина дуги НЕ определяется абсолютным значением разности углов. Это просто разница углов, конец минус начало, нормализованная до [0, 2π) (если результат отрицательный, добавьте 2π).   -  person n. 1.8e9-where's-my-share m.    schedule 11.07.2012


Ответы (3)


Во-первых, о ваших «sic» комментариях. Центральная точка окружности называется также и центром дуг, а движение против часовой стрелки является наиболее распространенным соглашением. Точно так же, как ось x указывает вправо, а ось y вверх, и углы начинаются с положительной оси x.

Чтобы узнать, находится ли точка x, y в области, определенной в полярных координатах (r, eta), вы просто конвертируете точку x_point, y_point в полярные координаты.

r_point=sqrt((x_point-x_circle)^2 + (y_point-y_circle)^2 )
eta_point=atan2((y_point-y_circle) , (y_point-x_circle))

Используйте atan2, тогда вам не нужно думать о знаках, пи-флипах и т. д. -c">в чем разница между atan и atan2 в c++?

Now, is the radious within the 'sector' ?
if (r_point<r_sector) ... 

Если это так, то стоит взглянуть на угловую часть: вычтите угол звезды как из eta_point, так и из углового размера сектора.

eta_point_new = eta_point - ang1
ang2_new = ang2 - ang1

Теперь ang2_new показывает, насколько велик сектор в направлении вращения, а eta_point_new показывает, насколько далеко находится точка. Если ang2_new отрицательное, это означает, что сектор пересек границу вашей угловой координаты, поэтому вам нужно добавить к нему 2pi. Затем:

if (eta_point_new < ang2_new) 
   ... then the point is inside...

Мне жаль, что у меня не было времени протестировать это или написать на правильном C++, делайте с этим что хотите.

person Johan Lundberg    schedule 10.07.2012

Положительное направление вращения против часовой стрелки. Я даю псевдокод, а не настоящий код C++.

Чтобы определить угол поворота, вычтите начальный угол из конечного угла. Не принимайте абсолютное значение. Нормировать к интервалу [0, 2).

rot_angle = end_angle - start_angle;

while (rot_angle < 0)   
  rot_angle += TWO_PI;

Теперь нам нужно взять точку на полпути между центром и начальной точкой дуги и повернуть ее вокруг центра на половину общего угла поворота, который мы только что нашли:

start_point = rotate (point (center.x+r, center.y), center, start_angle);
interior_point = rotate (midpoint (center, start_point), center, rot_angle/2);

Чтобы повернуть точку pt вокруг начала координат o на угол theta:

point rotate (point pt, point o, double theta)
{
  return point(cos(theta) * (pt.x-o.x) - sin(theta) * (pt.y-o.y) + o.x,
               sin(theta) * (pt.x-o.x) + cos(theta) * (pt.y-o.y) + o.y);

}

Для полноты:

point midpoint (point p1, point p2)
{
   return point((p1.x+p2.x)/2, (p1.y+p2.y)/2);
}
person n. 1.8e9-where's-my-share m.    schedule 10.07.2012

РЕШЕНО:

1) Рассчитать центр дуги:

double e = GetEndAngle();
double s = GetStartAngle();
double d = e - s;

double arc_center_x = 0.0;
double arc_center_y = 0.0;
double offset = 0.0;
if(d < 0.0) {
    offset = PI;
}
arc_center_x = (GetPosition().GetX() + std::cos(((s + e) / 2.0) + offset) * GetRadius());
arc_center_y = (GetPosition().GetY() + -std::sin(((s + e) / 2.0) + offset) * GetRadius());
_center = Vector2D(x, y);

2) Рассчитать линию от этого центра до положения сектора:

Line l(arc_center_x, arc_center_y, p_x, p_y);

3) Получите середину этой линии, которая всегда находится внутри углов сектора:

double x = l.GetCenter().GetX();
double y = l.GetCenter().GetY();
person Casey    schedule 29.07.2012