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

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

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

rami

У вас получилось почти как у меня, единственное хочу заметить, что вместо двух строчек:
Dim Point
Point = createUnoStruct("com.sun.star.awt.Point")

лучше писать одну:
Dim aPoint As New "com.sun.star.awt.Point"
так меньше кода и переменная сразу создаётся как структура.

Вот моя функция:
Sub circle(oDoc, oDrawPage, x, y, r, color)
Dim oShape, aPoint As New "com.sun.star.awt.Point", aSize As New "com.sun.star.awt.Size"
oShape = oDoc.createInstance("com.sun.star.drawing.EllipseShape")
aPoint.X = x - r
aPoint.Y = y - r
aSize.Width = 2 * r
aSize.Height = 2 * r
oShape.setPosition(aPoint)
oShape.setSize(aSize)
oShape.FillColor = color
oDrawPage.add(oShape)
End Sub



Цитата: Tigrik от 12 марта 2020, 20:21Правильно ли я понимаю, что лепестки нарисованы, скорее всего, с помощью ClosedBezierShape?
Да, иначе вы её не закрасите. На каждый лепесток нужно 6 точек, все они могут быть типа CONTROL (но я не знаю, это нормально или фича)

Цитата: Tigrik от 12 марта 2020, 20:21Некоторая сложность - вывести формулу для вычисления самих координат точек, но... надо подумать.
Вычислить координаты точки в зависимости от угла и радиуса — это 5 класс. Формула в одну строчку (всего две строчки для X и Y)

В целом вы на правильном пути.

Tigrik

#16
Да, так эти лепестки рисуются ГОРАЗДО легче.


Sub petals(oDoc, oDrawPage, x, y, r, n, color)
'Этот макрос будете писать после того, как получится рисовать круги (этот макрос содержит всего лишь 15 строк кода)
'    УРА!!! РАБОТАЕТ!!!
Dim oShape, Point As New "com.sun.star.awt.Point", i%
Dim oCoords As New "com.sun.star.drawing.PolyPolygonBezierCoords"   '   Координаты точек для кривой Безье
Dim CoordPetal(n * 6) As New com.sun.star.awt.Point     '  Координаты точек "лепестка"
Dim Flags(n * 6)                                 '  Флаги точек
For i = LBound(CoordPetal) To UBound(CoordPetal)
' Заполняем фактическими координатами.
Point.X = x + r(i MOD 2) * SIN((360 / 6 / n) * i * (Pi / 180))
Point.Y = y - r(i MOD 2) * COS((360 / 6 / n) * i * (Pi / 180))
CoordPetal(i) = Point
' Заполняем флагами
If  (i MOD 3) = 0 Then Flags(i) = 0 Else Flags(i) = 2
Next
oCoords.Coordinates = Array( CoordPetal )
oCoords.Flags = Array( Flags)
oShape = oDoc.createInstance("com.sun.star.drawing.ClosedBezierShape")
oDrawPage.add(oShape)
oShape.PolyPolygonBezier = oCoords
oShape.FillColor = color
End Sub

Правильно, так и должно быть?

=========
Но вопрос про "зеркало" для Фигур, а, также, есть ли возможность выносить центр вращение фигуры за границы самой фигуры - остался открытым!

rami

Переменная Point As New "com.sun.star.awt.Point" не нужна, есть же массив структур CoordPetal(n * 6) As New com.sun.star.awt.Point, да и градусы не нужны.
Вместо:
Point.X = x + r(i MOD 2) * SIN((360 / 6 / n) * i * (Pi / 180))
Point.Y = y - r(i MOD 2) * COS((360 / 6 / n) * i * (Pi / 180))
CoordPetal(i) = Point

можно:
CoordPetal(i).X = x+r(i Mod 2)*cos(i*pi/3/n)
CoordPetal(i).Y = y+r(i Mod 2)*sin(i*pi/3/n)

Tigrik

Цитата: rami от 13 марта 2020, 12:59Переменная Point As New "com.sun.star.awt.Point" не нужна, есть же массив структур CoordPetal(n * 6) As New com.sun.star.awt.Point, да и градусы не нужны.
Градусы я оставил для наглядности, поэтому не стал сокращать цифры.
А с Point - это да, я "погорячился" - уже автоматически ставлю, даже куда не нужно.

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

mikekaganski

Хм. То есть "(360 / 6 / n) * i * (Pi / 180)" нагляднее, чем "i * 2*Pi/(6*n)" - то есть прямо по-русски написанного "i-тая точка на окружности, поделённой на 6n"?
С уважением,
Михаил Каганский

Tigrik

#20
Цитата: mikekaganski от 13 марта 2020, 13:36Хм. То есть "(360 / 6 / n) * i * (Pi / 180)" нагляднее, чем "i * 2*Pi/(6*n)" - то есть прямо по-русски написанного "i-тая точка на окружности, поделённой на 6n"?

Я не успел исправить/дописать своё предыдущее сообщение, тогда оформлю как новое.

И ещё один небольшой вопрос - вспомнил об этом и решил дописать - по теме градусов.
В первой своей части, я использую глобальную переменную и в самом начале инициирую её (ещё и поэтому я не сокращал выражение, что бы там проставить переменную):
dDegRad = Pi / 180
Хотел объявить её константой, но не получается - ругается "Синтаксическая ошибка Basic", указывая на цифру "180".
Я понял, что нельзя константе присвоить выражение. Или как-то можно?

---
И для меня, первое выражение было более нагляднее.
Для меня, было более логичнее выражение (360/n)/6 * i * (Pi/180), где (360/n)/6 - приращение угла каждой i-ой точки при задание n лепестков.

mikekaganski

Это потому, что число "360" магическим образом ассоциируется со словом "окружность", а "2π" почему-то нет ;)
С уважением,
Михаил Каганский

rami

Цитата: Tigrik от 13 марта 2020, 13:55Я понял, что нельзя константе присвоить выражение. Или как-то можно?
Если в выражении будут только цифры:
Const dDegRad =  3.14159265358979 / 180

mikekaganski

#23
Цитата: Tigrik от 13 марта 2020, 13:55В первой своей части, я использую глобальную переменную и в самом начале инициирую её (ещё и поэтому я не сокращал выражение, что бы там проставить переменную):
dDegRad = Pi / 180
Хотел объявить её константой, но не получается - ругается "Синтаксическая ошибка Basic", указывая на цифру "180".
Я понял, что нельзя константе присвоить выражение. Или как-то можно?

Можно присвоить выражение константе. Нужно только чтобы это выражение было вычисляемо во время компиляции, а для этого все входящие туда символы сами должны быть константами. А Pi, как ни забавно, не константа. Вы всегда можете присвоить ей значение, и поступать с ней как в военное время ;)

Сравните:


const ccc = 2 / 3
const ddd = ccc + 1
pi = 4
ccc = 4
С уважением,
Михаил Каганский

rami

Цитата: mikekaganski от 13 марта 2020, 14:19Вы всегда можете присвоить ей значение, и поступать с ней как в военное время
Ругается на pi = 3Ошибка чтения.Свойство только для чтения. (LibreOffice 6.4.1.2)

mikekaganski

#25
Version: 6.4.2.1 (x64)
Build ID: c92dba0b4728c0ec26c4b83e2c0fbf3284425375
CPU threads: 12; OS: Windows 10.0 Build 18363; UI render: GL; VCL: win;
Locale: ru-RU (ru_RU); UI-Language: en-US
Calc: CL

EDIT: ага, вижу. Моя ошибка - времени компиляции; Ваша - времени выполнения.
А в VBA работает...

Тем не менее, это дела не меняет: Pi - это не константа; даже если это "свойство только для чтения", это не делает его вычислимым во время компиляции :)
С уважением,
Михаил Каганский

rami

Цитата: mikekaganski от 13 марта 2020, 14:32Тем не менее, это дела не меняет: Pi - это не константа
А Справка говорит, что константа типа Double. (для сведения)

mikekaganski

#27
Цитата: rami от 13 марта 2020, 14:54
Цитата: mikekaganski от 13 марта 2020, 14:32Тем не менее, это дела не меняет: Pi - это не константа
А Справка говорит, что константа типа Double. (для сведения)

Да, так и написано. И лучше бы так и было.
Я полагаю, что это не сделано технически константой исходя из кросс-платформенности пакета, из-за которого константа, определённая на одной машине, может теоретически оказаться не лучшим возможным приближением числа π на другой. А предкомпилированный результат выполняется, например, при запароленной библиотеке, и там использовалось бы жёстко заданное значение...
С уважением,
Михаил Каганский

Tigrik

Спасибо за помощь и, для себя, я выбрал такое решение:

Const Pi = 3.14159265358979
Const dDegRad = Pi / 180

Проверил - работает.

Tigrik

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

В продолжение этой темы (и, так называемого, "проекта" Шри Янтра), появилось некоторое продолжение.
В принципе, оставалось самое легкое - Внешний Контур. Он сильно "ломаный", но много-симметричный - главное, определить координаты первых пяти точек (я начал с верхнего левого угла).
Сразу же, пришли два варианта: первый - как с "лепестками": вычислить вектора этих точек и по определенному циклу установить остальные точки массива координат. Второй - даже легче: нарисовать квадрат, который в центре и, затем, 4 пары прямоугольников, которые образуют выступы в Контуре.
Всё работает, но решил пойти, как всегда, более трудным путем (хоть и интересным) - через отражения.
Так и не смог найти этот метод (хотя в ЛО есть такая возможность как отразить фигуры - по горизонтали или вертикали) - предполагаю, что в макросах этим мало кто пользуется.
Пришлось самому "соорудить" такую функцию. Она определяет координаты отраженной точки: задается центр построения, угол линии для отражения (против часовой стрелки) и сама точка (эта точка, и которая получается - в абсолютных координатах и в структурах ("...awt.Point").
Но столкнулся с "непонятками" с функцией MOD (остаток от деления). В таблицах Calc она работает как нужно (насколько я могу себе это представить), а в Basic выдает другой результат. На примере: Calc: -57 MOD 360 результат 303, а Basic: -57 MOD 360 результат -57.
Не знаю какой вариант правильно решается, но для моей программы подходит первый вариант. К тому же, сама функция возвращает значение округленное до целого - это снижает точность расчета. Сделал свой вариант этой функции.

'  Функция определения остатка от деления
'  делимое, делитель, количество разрядов для сохранения результата
Function ModDoubleDigits(NumberInput#, NumberMod#, Digits&) As Double
Dim dg&
dg = 10^Digits
ModDoubleDigits = ((NumberInput * dg + NumberMod * dg) Mod NumberMod * dg) / dg
End Function

======
Сама функция отражения точки получилась не совсем "легкая", так как уверен, что есть более "изящное" и простое решение. Возможно, что в этом случае, не нужна и дополнительная функция (MOD для углов).
Эту функцию я уже завел в свой макрос - всё работает, за пару дней тестирования ни разу не подвела.

'   Функция для отражения точки
'   Задается центр, через который проходит линия отражения,
'   Координаты точки в структуре и угол линии отражения, в направлении против часовой стрелки
Function MirrorPoint(CenterXX, CenterYY, CoordPoint As "com.sun.star.awt.Point", AngleMirror#) As "com.sun.star.awt.Point"
 Dim CoordMirror As New com.sun.star.awt.Point
 Dim Quadrant% '   Квадрант, в котором находится заданная Точка
 Dim LenVector# '   Длина Вектора Точки
 Dim AngleVector# '   Угол Вектора Точки
 Dim AngleReflect# '   Угол Отражения
 Dim xP#, yP# '   Координаты заданной Точки
 Dim Z1, Z2
AngleMirror = ModDoubleDigits(AngleMirror, 360, 4)
  xP = CoordPoint.X - CenterXX
  yP = CoordPoint.Y - CenterYY
'   Определяется Квадрант (если точка в центре построения - результат 0)
If xP > 0 Then
If yP >= 0 Then Quadrant = 4 Else Quadrant = 1
ElseIf xP < 0 Then
If yP >= 0 Then Quadrant = 3 Else Quadrant = 2
ElseIf yP > 0 Then Quadrant = 4
ElseIf yP < 0 Then Quadrant = 1  Else Quadrant = 0
End If
'   Вычисляются Длина Вектора Точки и его Угол
LenVector = Sqr(xP ^ 2  +  yP ^ 2)
If Quadrant = 0 Then
AngleVector = 0
Else AngleVector = dArcCOS(Abs (xP) / LenVector) / dDegRad
End If
'   В зависимости от Квадранта, определяется Угол Вектора в диапазоне (0; 359)
Z1 = 180 * (1 - ((Quadrant Mod 3) Mod 2))
Z2 = (((Quadrant * -1 Mod 2) + 3) MOD 3) - 1
AngleVector = ModDoubleDigits((Z1 + Z2 * AngleVector), 360, 4)
'   Определяется угол, относительно которого отражается точка
'   Между Вектором и Линией Зеркала угол должно быть не больше 90 градусов
AngleReflect = ABS(AngleMirror - AngleVector)
If AngleReflect > 90 And AngleReflect < 270 Then AngleMirror = AngleMirror + 180 Else AngleMirror = AngleMirror
AngleMirror = ModDoubleDigits(AngleMirror, 360, 4)
'   Величина Угла Отражения (угол между заданным Вектором и отраженным)
AngleReflect = ModDoubleDigits(2 * AngleMirror - AngleVector, 360, 4)
'   Полученные координаты отраженной точки
CoordMirror.X = CenterXX + COS(AngleReflect * dDegRad) * LenVector
CoordMirror.Y = CenterYY + SIN(AngleReflect * dDegRad) * LenVector * (-1)
MirrorPoint = CoordMirror
End Function

Хочу попросить, по возможности и при желание, посмотреть, есть ли какие-нибудь ошибки. Кто-то, возможно, опытным и профессиональным взглядом уведет недочёты. Может быть, есть и готовые решения или варианты улучшить эту функцию.
Я оставил комментарии и некоторые лишние выражения для удобства - это всегда можно убрать.