Вычислить координаты вершин правильного многоугольника

Я пишу программу, в которой мне нужно рисовать многоугольники с произвольным числом сторон, каждый из которых преобразуется по заданной формуле, которая динамически меняется. Здесь задействована довольно интересная математика, но я застрял на этом вопросе.

Как вычислить координаты вершин правильного многоугольника (того, в котором все углы равны), учитывая только количество сторон и в идеале (но не обязательно) имея начало координат в центре ?

Например: шестиугольник может иметь следующие точки (все floats):

( 1.5  ,  0.5 *Math.Sqrt(3) )
( 0    ,  1   *Math.Sqrt(3) )
(-1.5  ,  0.5 *Math.Sqrt(3) )
(-1.5  , -0.5 *Math.Sqrt(3) )
( 0    , -1   *Math.Sqrt(3) )
( 1.5  , -0.5 *Math.Sqrt(3) )

Мой метод выглядит так:

void InitPolygonVertexCoords(RegularPolygon poly)

и к этому нужно добавить координаты (или что-то похожее, вроде списка):

Point[] _polygonVertexPoints;

Меня в основном интересует алгоритм здесь, но примеры на С# были бы полезны. Я даже не знаю, с чего начать. Как мне это реализовать? Это вообще возможно?!

Спасибо.


person Nobody    schedule 08.08.2010    source источник
comment
Интересный побочный факт: не существует правильного многоугольника (кроме квадрата) с целочисленными координатами (доказательство)   -  person Martin Thoma    schedule 14.03.2013
comment
это не программирование/кодирование, это геометрия!   -  person antony.trupe    schedule 05.09.2013


Ответы (7)


for (i = 0; i < n; i++) {
  printf("%f %f\n",r * Math.cos(2 * Math.PI * i / n), r * Math.sin(2 * Math.PI * i / n));
}

где r - радиус описанной окружности. Извините за неправильный язык Нет Habla C#.

В основном угол между любыми двумя вершинами составляет 2 pi / n, и все вершины находятся на расстоянии r от начала координат.

РЕДАКТИРОВАТЬ: Если вы хотите, чтобы центр был не в начале координат, скажем, в (x,y)

for (i = 0; i < n; i++) {
  printf("%f %f\n",x + r * Math.cos(2 * Math.PI * i / n), y + r * Math.sin(2 * Math.PI * i / n));
}
person deinst    schedule 08.08.2010
comment
Чтобы обобщить, я бы добавил XC и YC (координаты центра круга) и угол 1-й вершины A к терминам cos/sin: px = xc + r * cos(2 * pi * i / n + A ); py = yc + r * sin(2 * pi * i/n + A); - person ysap; 09.08.2010
comment
Он спросил происхождение как центр. - person deinst; 09.08.2010
comment
Вау, не ожидал ответа так быстро. Итак, r — это расстояние от начала координат до любой из вершин, верно? И я предполагаю, что n - это количество сторон. Думаю, я понял... Спасибо - первоклассный ответ :-) - person Nobody; 09.08.2010
comment
@стоматолог - он сказал не обязательно - person ysap; 09.08.2010
comment
@rmx: Ну, это задача по математике, а не по программированию. Я думаю, вы знаете уравнение окружности x^2 + y^2 = r^2. Однако, чтобы превратить это в программу, вы должны разделить x и y. Это параметрическое уравнение делает следующее: { x = r * cos(theta), y = r * sin(theta), where 0 <= theta < 2 * PI }. Чтобы создать многоугольник с n сторонами, просто присвойте тета n различных значений. Чтобы сделать правильные многоугольники, просто выполните 2 * PI / n * i, где 0 ‹= i ‹ n. Пожалуйста, обратитесь к Параметрическое уравнение — Википедия для получения дополнительной информации. - person Siu Ching Pong -Asuka Kenji-; 09.08.2010
comment
Спасибо, Аска, теперь у меня все работает очень хорошо. Я использовал этот метод с double circumcircleRadius = (poly.SideLength / Math.Sin(angle)) / 2;, чтобы получить значение r. На самом деле это намного проще, чем казалось сначала. - person Nobody; 09.08.2010
comment
А что, если вам нужно определенное вращение в двухмерном координатном пространстве? Т.е. вместо того, чтобы просто выдавать координаты с заданным количеством сторон и радиусом, сделайте то же самое, учитывая то же самое + значение поворота. - person Alexander Ryan Baggett; 28.01.2015

Количество точек равно количеству сторон.

Вам нужен угол angle = 2 * pi / numPoints.

Затем, начиная вертикально над началом координат, размер полигона определяется как radius:

for (int i = 0; i < numPoints; i++)
{
    x = centreX + radius * sin(i * angle);
    y = centreY + radius * cos(i * angle);
}

Если ваш центр является источником, просто игнорируйте термины centreX и centreY, так как они будут равны 0,0.

Поменяв местами cos и sin, первая точка будет указывать горизонтально справа от начала координат.

person ChrisF    schedule 08.08.2010
comment
Должно быть sin(i + angle), а не так, как написано! - person ysap; 09.08.2010
comment
@ysap - ты уверен? Это дает точки в 0, угол, 2*угол, 3*угол и т. д. по кругу. Таким образом, для квадрата (4 точки, угол = 90) вы получаете точки 0, 90, 180 и 270. - person ChrisF; 09.08.2010
comment
Извините, я думал, что прочитал в вашем посте, что угол был смещением. Перечитав его (и предполагая, что вы не редактировали его после моего комментария), вы правы, как это выглядит сейчас. В любом случае, добавление термина Angle к аргументу sin/cos (который на самом деле является фазой) сделает место 1-й точки произвольным. - person ysap; 09.08.2010
comment
@ysap - кажется, у меня был краткий комментарий о смещении угла, но я понял, что это сбивает с толку, и удалил его. - person ChrisF; 09.08.2010
comment
Спасибо @ChrisF, теперь я понимаю, почему школьная математика так важна. Еще раз спасибо :) - person Jayshil Dave; 15.06.2012
comment
@ChrisF, что, если вы хотите, чтобы шестиугольник повернулся так, чтобы его вершина была не заостренной, а плоской? Визуальное отличие представлено здесь: redblobgames.com/grids/hexagons/#basics - person Sir; 31.08.2017

Извините, у меня сейчас нет под рукой полного решения, но вы должны попробовать поискать 2D-рендеринг кругов. Все классические реализации круга (x, y, r) используют многоугольник, как вы описали для рисования (но с более чем 50 сторонами).

person Rock    schedule 08.08.2010

Скажем, расстояние вершин до начала координат равно 1. И скажем, (1, 0) всегда является координатой многоугольника.

Учитывая количество вершин (скажем, n), угол поворота, необходимый для позиционирования (1, 0) до следующей координаты, будет равен (360/n).

Требуемое здесь вычисление состоит в вращении координат. Вот что это такое; Матрица вращения.

Скажем, тета = 360/n;

[cos(theta) -sin(theta)]
[sin(theta) cos(theta)]

будет ваша матрица вращения.

Если вы знаете линейную алгебру, вы уже знаете, что я имею в виду. Если нет, просто взгляните на Умножение матриц

person tafa    schedule 08.08.2010

Одна из возможных реализаций для создания набора координат для правильного многоугольника:

Задайте центр многоугольника, радиус и первую вершину1.
Поверните вершину n раз 2 под углом: 360/н.

В этой реализации я использую вектор для хранения сгенерированных координат и рекурсивную функцию для их генерации:

void generateRegularPolygon(vector<Point>& v, Point& center, int sidesNumber, int radius){
    // converted to radians
    double angRads = 2 * PI / double(sidesNumber);
    // first vertex  
    Point initial(center.x, center.y - radius);
    rotateCoordinate(v, center, initial, angRads, sidesNumber);
}

где:

void rotateCoordinate(vector<Point>& v, Point& axisOfRotation, Point& initial, double angRads, int numberOfRotations){
    // base case: number of transformations < 0
    if(numberOfRotations <= 0) return;
    else{
        // apply rotation to: initial, around pivot point: axisOfRotation
        double x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
        double y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
        // store the result
        v.push_back(Point(x, y));
        rotateCoordinate(v, axisOfRotation, Point(x,y), angRads, --numberOfRotations);
    }
}

Примечание:

Point — это простой класс для переноса координат в единую структуру данных:

class Point{
public:
    Point(): x(0), y(0){ }
    Point(int xx, int yy): x(xx), y(yy) { }
private:
    int x;
    int y; 
}; 

1 относительно (относительно) центра, радиуса. В моем случае первая вершина перемещается из центра вверх по горизонтали на длину радиуса.

2 n-правильных многоугольника имеют n вершин.

person Ziezi    schedule 23.09.2015

Простой метод: возьмем N (количество сторон) и длину стороны L. Угол будет T = 360/N. Допустим, одна вершина находится в начале координат.

* First vertex = (0,0)
* Second vertex = (LcosT,LsinT)
* Third vertex = (LcosT+Lcos2T, LsinT+Lsin2T)
* Fourth vertex = (LcosT+Lcos2T+Lcos3T, LsinT+Lsin2T+Lsin3T)

Вы можете сделать цикл for

person samir    schedule 20.11.2010

хм, если вы протестируете все версии, перечисленные здесь, вы увидите, что реализация не очень хороша. вы можете проверить расстояние от центра до каждой сгенерированной точки многоугольника с помощью: http://www.movable-type.co.uk/scripts/latlong.html

Теперь я много искал и не смог найти хорошей реализации для вычисления многоугольника с использованием центра и радиуса ... поэтому я вернулся к учебнику по математике и попытался реализовать это самостоятельно. В конце концов я придумал это... что на 100% хорошо:

            List<double[]> coordinates = new List<double[]>();
            #region create Polygon Coordinates
            if (!string.IsNullOrWhiteSpace(bus.Latitude) && !string.IsNullOrWhiteSpace(bus.Longitude) && !string.IsNullOrWhiteSpace(bus.ListingRadius))
            {
                double lat = DegreeToRadian(Double.Parse(bus.Latitude));
                double lon = DegreeToRadian(Double.Parse(bus.Longitude));
                double dist = Double.Parse(bus.ListingRadius);
                double angle = 36;

                for (double i = 0; i <= 360; i += angle)
                {
                    var bearing = DegreeToRadian(i);

                    var lat2 = Math.Asin(Math.Sin(lat) * Math.Cos(dist / earthRadius) + Math.Cos(lat) * Math.Sin(dist / earthRadius) * Math.Cos(bearing));
                    var lon2 = lon + Math.Atan2(Math.Sin(bearing) * Math.Sin(dist / earthRadius) * Math.Cos(lat),Math.Cos(dist / earthRadius) - Math.Sin(lat) * Math.Sin(lat2));

                    coordinates.Add(new double[] { RadianToDegree(lat2), RadianToDegree(lon2) });

                }

                poly.Coordinates = new[] { coordinates.ToArray() };
            }
            #endregion

Если вы проверите это, вы увидите, что все точки находятся на точном расстоянии, которое вы указали (радиус). Также не забудьте объявить earthRadius.

private const double earthRadius = 6371.01;

Это вычисляет координаты десятиугольника. Вы видите, что используемый угол равен 36 градусам. Вы можете разделить 360 градусов на любое количество сторон и поместить результат в переменную угла. В любом случае .. я надеюсь, что это поможет вам @rmx!

person danvasiloiu    schedule 26.01.2015