Построение фигур в Basic(LibreOffice)

Автор Tigrik, 2 марта 2020, 02:13

0 Пользователи и 1 гость просматривают эту тему.

Tigrik

Скорее всего, я изначально поместил свой вопрос не в тот раздел, поэтому и не было ответов.
Попытка расспросить:
https://forumooo.ru/index.php/topic,7985.0.html
---------

За это время (почти месяц прошёл) многое успел узнать по этой теме, что-то даже получилось "сваять", но основной вопрос так и остался без ответа.
А именно: есть ли в LibreOffice возможность макросами строить такую базовую фигуру, как, например, равнобедренный треугольник?
Автозапись макроса в ЛО показывает, что, вроде бы, что-то подобное присутствует,

dim args1(7) as new com.sun.star.beans.PropertyValue
args1(0).Name = "BasicShapes"
args1(0).Value = "isosceles-triangle"
...
dispatcher.executeDispatch(document, ".uno:BasicShapes", "", 0, args1())

но ничего конкретного так и не удалось узнать.
Автозапись макросов, именно по построению базовых фигур, в ЛО мне в этом вопросе не помогла разобраться.

Ровно неделю "рыскал" в Инете за полезной по этой теме информацией, но так и ничего не нашёл (как выяснилось плохо искал!).
На мой любительский взгляд, два очень информативных сайта:
https://api.libreoffice.org/docs/idl/ref/index.html
https://www.openoffice.org/api/docs/common/ref/com/sun/star/drawing/module-ix.html
но мои скудные знания английского языка и, практически, полное отсутствие понимания самого Basic(OO и LO) не позволили оценить полезность этой информации, именно для меня.
На этих сайтах, я так и не смог ПОЛНОСТЬЮ разобраться - что к чему и как это применить.
Нашёл книгу Питоньяка, но на английском языке - там увидел нужную информацию, но не совсем её понял.

Пришлось обратиться к Excel (он, как и OpenOffice, были установлены у меня в компе, но не использовались - только один раз, для проверки совместимости таблиц).

---------
Подумалось, что про макрос в Excel и сам "проект" лучше вынести в отдельное сообщение.

Tigrik

Если администрации форума или модератору это сообщение увидеться не уместным на полях этого форума (или этой темы, или раздела), то, естественно, его можно удалить.
Но я постараюсь не далеко уходить от темы вопроса (вопросов).

Два слова о самом "проекте", что бы было удобнее объяснять и расспрашивать некоторые моменты.
Есть такая тема: Шри Янтра. Можно "погуглить", кто не совсем знает, что это, но, буквально тезисно:
Шри Янтра - это самая известная янтра. В свою очередь, янтры - это мандалы, то есть геометрические рисунки, которые несут какую-то сакральную эзотерическую суть.
Используются как инструмент для концентрации внимания, медитации и подобных действий (как сами рисунки, так и само построение янтр, мандал).

Я, давно, хотел научиться построению Шри Янтры, но только на днях на глаза попался алгоритм одного из вариантов (этих вариантов - множество) построения этой мандалы.
Условно, можно поделить сам рисунок на три части: внутренняя - пересекающиеся треугольники; средняя - два "слоя" "лепестков" (иногда, используются дуги); внешний контур (много разных вариантов).
Внутреннюю часть, по предложенному алгоритму, в AutoCAD мне удалось нарисовать, буквально, за 15 минут. Плюс "раскраска" в Paint - еще 5 минут.
Но мне стало интересно, можно ли написать макрос (в той же Либре), что бы это рисовалось программно. Дня три, у меня, ушло на расчёт точек треугольников, в зависимости от радиуса основной окружности куда вписана группа этих треугольников.
Ну а дальше ... дальше - "засада". На самой странице ЛО базовые фигуры не программируются (в параметры нельзя записать формулы) и само-запись не помогла.

В Excel мне потребовалось всего 3 дня, что бы получился более-менее (в нужном мне объеме, скорее, более) нормальный и работающий макрос.
Он совсем простенький и, возможно, что 3 дня - это слишком много для этого макроса, но сказалось то, что последний раз я "макросил" на Экселе больше 20 лет назад, да и то - немного.
Сам расчёт значений для построения фигур происходит в таблице на одном из листов документа (а фигуры строятся в другом листе) и по заданному радиусу основной окружности, а макрос только "снимает" эти данные из таблицы и рисует на их основе.
Можно было бы все расчеты "занести" в макрос, но, всё, что я хотел от Экселя, я получил, и хотелось вернуться в Либре и попробовал адаптировать макрос в Либре, но не получилось - принципиально не строились треугольники - все остальное: линии, окружности, квадраты рисовались, а треугольники нет.
Кстати, выяснилось, что Либре "проводит" настоящую "русофобскую политику" - все кириллические символы "запикали" (или это Эксель так эти символы "коверкает", что Либре их "не понимает"?).

------
Прицепил файл с макросом и нужными расчетами.
Мне не жалко - если кому-то будет интересно (а, может быть, и полезно), то можно спокойно пользоваться.

---
Не получилось отправить, так как заметил, что файлы с макросами нельзя - он будет в архиве к следующему сообщению.

Tigrik

Все-таки, хотелось узнать что это за "чудо дивное" (или "диво чудное") - макросы в Либре и, так получилось, на следующий день, "вышел" на переведенного Питоньяка. Тут дело пошло "повеселее". У него много полезного по моей теме - хоть и не все, пока, удалось понять, но кое-что применил в макросе. Якунина "подкинула" некоторую информацию по работе с файлами.
Сейчас, очень надеюсь, что временно, не работаю, поэтому получалось уделять этому процессу (программирование макроса) достаточно много времени - хотя постоянно отвлекали и сам отвлекался, но ровно две недели ушло на эту программку.
Она получилось чуть серьезнее, чем для Экселя, но прошу не судить слишком строго - это мой первый макрос в ЛО, к тому же, BasicOO(LO) пришлось осваивать с нуля.
Из этих двух недель (да и не каждый день), целых пять дней ушли на выяснение, как мне кажется, "детских вопросов".
Целый день пытался понять, как организовать структуру (объектную переменную), но так и не выяснил и начал "работать" с массивами. Только, после того, как закончил макрос, увидел решение и "перебрал" массив.
Другой день - никак не удавалось найти функцию или метод, что бы "взять" путь к файлу, который нужно сохранить. Получился, по моему мнению, "костыль" - "разбивка" строки пути в массив и новая "сборка".
Но больше всего, на целых три дня, "завис" над загрузкой библиотек и диалогов. У Питоньяка "подсмотрел" решение и оно заработало в макросе, но... Но до перезапуска программы - первый запуск макроса вызывает исключение (остановку макроса), а, затем, все последующие запуски проходят нормально (до следующего перезапуска).
С какого-то момента, я стал работать и из приложения, и из самого документа - подгружаются разные библиотеки и все это учитывалось в каждом из макросов.
Долго не мог понять, что функция LoadDialog - это не стандартный метод Basic, а функция из глобальной библиотеки макросов в приложении. Не знаю, правильно ли я понял работу этой функции, но я взял эту функции как основу, но изменил необязательный аргумент oLibContainer на такой же, но обязательный (и стал передавать в функцию тот контейнер библиотек, который нужен в данный момент); и убрал ненужную теперь проверку. Правильно или не правильно я понял, но исключение пропало. Может быть, кто-то объяснит эту ситуацию.

Последние два дня, я этот макрос "гонял" "вдоль и поперек" - ошибок, пока, не заметил.
Нюансы этого макроса в том, что там очень много лишнего: полно сообщений - для моего понимания и моей практики различных процедур и функций (но лишние сообщения я организовал в две отдельные подпрограммы, вход в которые можно "заремить" одним движением.
Где-то можно и алгоритм действия макроса улучшить, и подправить структуру некоторых подпрограмм. Если кто-то, в этом макросе, найдет какие-то ошибки, неточности или другие замечания, пожалуйста, напишите об этом.

------
Прицепил архив с двумя файлами (для Экселя и Либре) и некоторые результаты работы макросов - раскраска треугольников в Paint.
В файле для ЛО есть отдельно макрос для Экселя, но он не рабочий для Либре - он для информации.
Естественно, аналогично, как с экселевским макросом - кому интересно, тот может воспользоваться и макросом для ЛО.

Tigrik

Я даже сообщения, теперь (а, может быть, это было и раньше), стал писать как программу - отдельные сообщения-подпрограммы.
А в этом сообщении, я хотел задать вопросы, на которые я, пока, не смог найти решения - все они в рамках обсуждения обозначенной темы.
Если у меня будет время, возможность, а, главное, желание, то, скорее всего, я продолжу "программировать" Шри Янтру - дорисовывать остальные части.
С внешним контуром особых проблем не вижу - или полностью рисовать весь контур линиями (или полилиниями - это я, пока, не пробовал), но лучше и быстрее - одна восьмая контура, отразить зеркально, собрать это в группу и "размножить" три раза смещая на разные углы.
А вот "зеркалить" фигуры я, пока, и не разобрался как. Под большим вопросом и смещение фигуры (или группы фигур), так как центр смещения находится вне самой фигуры. Если кто сталкивался с этими вопросами или, просто, знает как это делать - подскажите, пожалуйста.
Если для внешнего контура это не большая проблема, то для "лепестков" будет тяжело каждый "лепесток" отдельно вырисовывать (а так - нарисовал "поллепестка", "отзеркалил" и "веером" пустил по кругу).

Отдельный вопрос о заливке фигур. Кстати, в начале макроса (в Main) есть возможность выбирать: заливать (и каким цветом) основную окружность, что бы уже был фон в центральной части Янтры.
А есть ли такой метод, который позволяет заливать цвет в указанную закрытую область, как это делает Paint, AutoCAD?
Или это надо самому рисовать закрытую фигуру и заливать её цветом? Это, конечно, можно, но очень муторно рассчитывать все 43 больших треугольников и, ещё, 12 малюсеньких (хотя, объективности ради, оригинальных треугольников в построение намного меньше, а остальные: или подобные, или отражены).

И, совсем, пока, мне не понятный момент - как найти функцию (алгебраическая имеется ввиду - зависимость координаты, допустим, Х от Y и радиуса окружности - наподобие квадратичной функций для параболы или гиперболы) для построения самой кривой этих "лепестков". Возможно, подойдет кривая Безье, но, пока, не понял.
Но, в конце концов, можно использовать и дуги, как это и используют в некоторых вариантов прорисовки Шри Янтры.

---------
Прошу прощения (а ведь начал "строчить" сообщения в прощенное воскресенье, но не успел закончить) за эту "длинную повесть" в четырех частях.

rami

В макросе OpenCreateDrawDoc разделитель пути для винды "\" (в двух местах) лучше заменить на GetPathSeparator().


В общем работает, но мне кажется, нужно поправить геометрию (чтобы пересечения линий были точками, а не маленькими треугольниками).

Tigrik

Цитата: rami от  2 марта 2020, 11:02
В макросе OpenCreateDrawDoc разделитель пути для винды "\" (в двух местах) лучше заменить на GetPathSeparator().


В общем работает, но мне кажется, нужно поправить геометрию (чтобы пересечения линий были точками, а не маленькими треугольниками).

Спасибо, за поправку.

Насчет геометрии. Данный просчет сделан на основе одного из вариантов (которых, как упоминалось множество): относительно легкое построение, но в результате - маленькие (или совсем малюсенькие) треугольники. В данном варианте - расчет правильный.
Есть и более сложные алгоритмы построения, где меньше таких "артефактов", но и там они присутствуют.
На данный момент, у меня есть ещё один вариант - его надо только просчитать и занести в макрос.
Планирую, если будет дальнейшее развитие "проекта", организовать форму в макросе, где можно будет определить необходимые параметры и атрибуты прорисовки Шри Янтры.

Цитата из одной из статей по построению Шри Янтры:
Цитировать
Шри-Янтра математически описывается системой четырех нелинейных алгебраических уравнений от четырех неизвестных до шестнадцатой степени по отдельной переменной и содержащих от 16 до 512 членов. Решение данного уравнения выше возможностей современного компьютера. Но и в этом случае при практических построениях можно говорить только об относительной точности, зависящей, например, от толщины линий.


Tigrik

Здравствуйте!

Продолжая начатую тему (Построение Фигур в Basic), образовался ещё один вопрос.
Фигура "Кривая Безье" характеризуется структурой из двух массивов, которые в простейшем случае (когда всего одна кривая) можно определить как: массив массивов (для координат точек) и массив для флагов.
Эту структуру можно заполнять при самом определение, если не так много точек, но, в моем случае, когда точек может быть очень много, пришлось это сделать по-другому: сначала в цикле заполнить вспомогательные массивы, а уже их использовать для непосредственного определения структуры. И всё работает замечательно.
Но это только в том случае, когда уже заранее известно количество точек. Но если количество точек будет меняться по ходу макроса (извне или по расчету), то, здесь, и происходит "заковыка".
С помощью DimDrray() можно менять размерность массивов, но только если они типа Variant. Массив флагов - простой массив и с ним нет проблем (в коде он и определяется с помощью DimDrray()), но другой массив - массив структур (там два параметра .X и .Y) - и вот с этим у меня, пока, не получается. Кстати, координаты точек мною придуманы только для этого кода - настоящая кривая более сложно строится.

Sub CreateCoordBezierCurves_FORUM()
Dim NumberPoints As Long   '   Количество точек для просчета
Dim oCoords                '   Координаты точек для кривой Безье
Dim i%
' Два массива, которые необходимы для заполнения структуры коор.точек
Dim CoordFirst(0 To 5) As New com.sun.star.awt.Point   '  Координаты точек
' Dim FlagsFirst(0 To 5)                                '  Флаги точек
Dim FlagsFirst()         '  Флаги точек (В этом варианте можно использовать DimDrray())
NumberPoints = 5
FlagsFirst = DimArray(NumberPoints)
For i = LBound(CoordFirst) To UBound(CoordFirst)
'   Заполняем фактическими координатами.
CoordFirst(i) = CreatePoint( 1000 * (i+1), 1000 * (SIN((i+1) * 13) ) + 1000 * (i+1))
'   Первые и последние точки - обычные точки, а средние - контрольные точки кривой Безье.
If  i=LBound(CoordFirst) Or i=UBound(CoordFirst) Then
FlagsFirst(i) = 0
Else FlagsFirst(i) = 2
End If
Next
'   Создаем объект со структурой для координат и флагов точек
oCoords = createUnoStruct("com.sun.star.drawing.PolyPolygonBezierCoords")
'   И заполняем её нашими массивами
oCoords.Coordinates = Array( CoordFirst )
oCoords.Flags = Array( FlagsFirst )
End Sub

Подскажите, пожалуйста, хотя бы, в какую сторону "копать".
И есть ли, вообще, решение этого вопроса?

rami

Цитата: Tigrik от 11 марта 2020, 23:34Подскажите, пожалуйста, хотя бы, в какую сторону "копать".
И есть ли, вообще, решение этого вопроса?
Копайте в сторону поверхности, вы сильно закопались ;D
У вас в принципе всё правильно, но:
1. по указанным точкам рисуется какая-то "сопля" (координаты точек очень не удачные), которая лично меня сильно удивила, я ожидал увидеть что-нибудь более близкое к тому, что нужно
2. код довольно "лохматый", например:
' Два массива, которые необходимы для заполнения структуры коор.точек
Dim CoordFirst(0 To 5) As New com.sun.star.awt.Point   '  Координаты точек
' Dim FlagsFirst(0 To 5)                                '  Флаги точек
Dim FlagsFirst()         '  Флаги точек (В этом варианте можно использовать DimDrray())
NumberPoints = 5
FlagsFirst = DimArray(NumberPoints)
For i = LBound(CoordFirst) To UBound(CoordFirst)

можно написать проще:
'Два массива, которые необходимы для заполнения структуры коор.точек
Dim NumberPoints%      'количество точек (размерность массивов)
NumberPoints = 5
Dim CoordFirst(NumberPoints) As New com.sun.star.awt.Point   '  Координаты точек
Dim FlagsFirst(NumberPoints)                                 '  Флаги точек
For i=0 To NumberPoints


Цитата: Tigrik от 11 марта 2020, 23:34Но это только в том случае, когда уже заранее известно количество точек. Но если количество точек будет меняться по ходу макроса (извне или по расчету), то, здесь, и происходит "заковыка".
Количество точек всегда известно заранее и должно оставаться неизменным до конца расчёта, вы хотите пять точек? с какого перепуга у вас в процессе вычислений должно получиться больше или меньше точек ???

Вот что у меня получилось:

Tigrik

rami, большое спасибо - и за подсказки, и за саму помощь.
Начну по порядку.

Эти точки, приведенные в примере, совсем "левые" - они только для примера - первое, что пришло в голову, что бы только были координаты и получалось что-то в виде кривой.
Для своих "лепестков" я выбрал обратную тригонометрическую функцию - y=арккосинус(x). Как я уже говорил, я только чуть больше месяца назад увидел как выглядит программирование в Basic и знаю, естественно, совсем мало. Поэтому, попал в "засаду" даже здесь.
Вроде бы как, в Basic можно задать арккосинус - y = ACOS(x), но нет - ругается. Я "наваял" небольшую функцию в одну строку, которая через арктангенс считает то, что мне нужно.

Function dArcCOS(X#) As Double
If X=-1 Then dArcCOS = Pi Else dArcCOS = 2 * ATN(Sqr((1 - X) / (1 + X)))
End Function


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

У Вас получалось очень хорошо, то что надо - и если у Вас будет желание, поделитесь в общих чертах алгоритмом. Это одна кривая в циклах, или использовались дополнительные методы (например, вращение)?

Лично у меня, планировался следующий алгоритм:
В верхней части, от вертикальной линии, рисовался один "полулепесток" (вправо). Озеркалить его влево и сгруппировать всё это. А, затем, "крутить" всю эту группу на определенный угол определенное количество раз, но центром вращения будет центр окружности, вдоль которой рисуются "лепестки".

Но как зеркалить, так и не нашел - хотя вроде бы есть MirrorAxis (LibreOffice 6.2 SDK API Reference), но не получилось это реализовать.
Но это я уже обошел - при расчете координат точек по функции арккосинуса, одновременно будут создаваться два массива точек (правого и левого "полулепестка").

Но как задать координаты центра, вокруг которого можно будет вращать фигуру - так и не нашел. Видимо, придется опять "костыли" придумывать (повернуть "лепесток" на нужный угол и перетащить в нужное место).

rami

Цитата: Tigrik от 12 марта 2020, 13:01Для своих "лепестков" я выбрал обратную тригонометрическую функцию - y=арккосинус(x)
Попробуйте "петлю Нестерова", должна подойти ;D . Не нужно ничего кроме синуса и косинуса.

Цитата: Tigrik от 12 марта 2020, 13:01Лично у меня, планировался следующий алгоритм:
В верхней части, от вертикальной линии, рисовался один "полулепесток" (вправо). Озеркалить его влево и сгруппировать всё это. А, затем, "крутить" всю эту группу на определенный угол определенное количество раз, но центром вращения будет центр окружности, вдоль которой рисуются "лепестки".
Вы очень сильно усложняете, всё намного проще. Задачу нужно разбить на более простые подзадачи.

Попробуйте забыть всё что делали до сих пор. Посмотрите на мою предыдущую картинку свежим взглядом, что видите? Я вижу четыре круга (один коричневый и три белых) и две фигуры с лепестками (жёлтая и красная) см. картинку "Растащить фигуры.png"

Нам нужно три макроса:
1. main — для общего управления (будет вызывать макросы circle и petals с соответствующими параметрами)
2. circle — для рисования одного круга
3. petals — для рисования одной фигуры с лепестками

Для начала попробуйте создать макрос circle рисующий круги, потом продолжим.

Для затравки:
Option Explicit
Const X0 = 10500, Y0 = 11000   'координаты общего центра
'радиусы кругов
Const R1 = 8250
Const R2 = 8000
Const R3 = 7000
Const R4 = 6000

Sub main
Dim oDoc, oDrawPage
oDoc = ThisComponent
oDrawPage = oDoc.getDrawPages().getByIndex(0)
circle(oDoc, oDrawPage, X0, Y0, R1, &Haa8855)  'коричневый круг для "кольца"
circle(oDoc, oDrawPage, X0, Y0, R2, &Hffffff)      'белый круг "дырка от кольца" и фон для жёлтых лепестков
'petals(oDoc, oDrawPage, X0, Y0, Array(R2, R3), 16, &Hfff000)   'жёлтые лепестки
circle(oDoc, oDrawPage, X0, Y0, R3, &Hffffff)      'белый круг "дырка от жёлтых лепестков" и фон для красных лепестков
'petals(oDoc, oDrawPage, X0, Y0, Array(R3, R4), 8, &Hff0000)    'красные лепестки
circle(oDoc, oDrawPage, X0, Y0, R4, &Hffffff)      'белый круг "дырка от красных лепестков"
End Sub

Sub petals(oDoc, oDrawPage, x, y, r, n, color)
'Этот макрос будете писать после того, как получится рисовать круги (этот макрос содержит всего лишь 15 строк кода)
End Sub

Sub circle(oDoc, oDrawPage, x, y, r, color)
'Сначала учитесь рисовать круги (этот макрос содержит всего лишь 10 строк кода)
End Sub

kompilainenn

Такими темпами может и фракталы можно рисовать в ЛО?
Поддержать разработчиков LibreOffice можно тут, а наш форум вот тут

mikekaganski

Цитата: kompilainenn от 12 марта 2020, 16:58
Такими темпами может и фракталы можно рисовать в ЛО?

Рисовать - конечно. Но вот только учитывая производительность, нарисовать - вряд ли ;)
С уважением,
Михаил Каганский

rami

Цитата: kompilainenn от 12 марта 2020, 16:58Такими темпами может и фракталы можно рисовать в ЛО?
Никогда не пробовал, но не  сомневаюсь что можно.


Цитата: mikekaganski от 12 марта 2020, 17:33Рисовать - конечно. Но вот только учитывая производительность, нарисовать - вряд ли ;)
Теоретически фракталы бесконечны, но на реальном мониторе элементы фрактала не могут быть меньше пикселя, думаю, что хватит производительности.

Tigrik

Цитата: rami от 12 марта 2020, 15:39Для начала попробуйте создать макрос circle рисующий круги, потом продолжим.


Sub circle(oDoc, oDrawPage, x, y, r, color)
'Сначала учитесь рисовать круги (этот макрос содержит всего лишь 10 строк кода)
'    Вроде бы, получилось!?
Dim oShape
Dim Point
Dim Size
Point = createUnoStruct("com.sun.star.awt.Point")
Point.X = x - r : Point.Y = y - r
Size = createUnoStruct("com.sun.star.awt.Size")
Size.Width = r * 2 : Size.Height = r * 2
oShape = oDoc.createInstance("com.sun.star.drawing.EllipseShape")
oDrawPage.add(oShape)
oShape.Position = Point
oShape.Size = Size
oShape.FillColor = color
End Sub


С кругами, вроде бы, я разобрался ещё в самом начале, когда учился рисовать треугольника. Правда, не оформил это в виде отдельной подпрограммы.

Правильно ли я понимаю, что лепестки нарисованы, скорее всего, с помощью ClosedBezierShape?
В цикле (по количеству лепестков), в структуру "раскладываются" координаты точек (наверное, 7 точек). Они поочередно лежат на внешней и внутренней окружностях через равные отрезки. Крайние и центральная точки (то есть 1, 4 и 7 точки) имеют флаг "NORMAL", а остальные - "CONTROL".
Во всяком случае, так, по-моему представлению, может получится. Некоторая сложность - вывести формулу для вычисления самих координат точек, но... надо подумать.
Хотя, допускаю, что я, опять, всё (или почти всё) усложняю! Я только учусь этому программированию ...

В любом случае, огромная благодарность Вам, rami, за помощь. Многое нашёл у Питоньяка, но что-то приходится самому и очень долго "копать".

Tigrik

Цитата: Tigrik от 12 марта 2020, 20:21В цикле (по количеству лепестков), в структуру "раскладываются" координаты точек (наверное, 7 точек). Они поочередно лежат на внешней и внутренней окружностях через равные отрезки. Крайние и центральная точки (то есть 1, 4 и 7 точки) имеют флаг "NORMAL", а остальные - "CONTROL".
Во всяком случае, так, по-моему представлению, может получится. Некоторая сложность - вывести формулу для вычисления самих координат точек, но... надо подумать.
Хотя нет, подумал, что удобнее делать в одном цикле, где каждая третья точка имеет флаг "NORMAL", а остальные - "CONTROL". А сами координаты легко считаются через синус и косинус, как и подсказал rami.
Будет время, займусь этим.