Разве так можно?
Например, решил использовать в своём проекте (указатель азимута) подходящий код, где необходимо “рисовать стрелки”.
Нагуглил код “стрелочные часы”.
Вот кусочек кода рисования стрелок оттуда:
// draw bold hand = minute hand and hour hand
void draw_hand_bold (int hand_angle, int hand_lenght_long, int hand_legth_short, int hand_dot_size) {
float xpos;
float ypos;
float xpos2;
float ypos2;
float tri_xoff;
float tri_yoff;
// calculate positions of the two circles
xpos = round(center_x + sin(radians(hand_angle)) * hand_lenght_long); // calculate x pos based on angle and radius
ypos = round(center_y - cos(radians(hand_angle)) * hand_lenght_long); // calculate y pos based on angle and radius
xpos2 = round(center_x + sin(radians(hand_angle)) * hand_legth_short); // calculate x pos based on angle and radius
ypos2 = round(center_y - cos(radians(hand_angle)) * hand_legth_short); // calculate y pos based on angle and radius
tri_xoff = round( sin(radians(hand_angle + 90)) * hand_dot_size);
tri_yoff = round(-cos(radians(hand_angle + 90)) * hand_dot_size);
u8g2.drawLine(center_x, center_y, xpos2, ypos2); // draw the line from one circle to the center
u8g2.drawDisc(xpos, ypos, hand_dot_size); // draw filled white circle
u8g2.drawDisc(xpos2, ypos2, hand_dot_size); // draw filled white circle
// two filled triangles are used to draw a rotated rectangle between two circles
u8g2.drawTriangle(xpos + tri_xoff, ypos + tri_yoff,
xpos - tri_xoff, ypos - tri_yoff,
xpos2 + tri_xoff, ypos2 + tri_yoff);
u8g2.drawTriangle(xpos2 + tri_xoff, ypos2 + tri_yoff,
xpos2 - tri_xoff, ypos2 - tri_yoff,
xpos - tri_xoff, ypos - tri_yoff);
}
Например, функция drawTriangle ожидает все переменные в int16_t (смотрел в исходнике).
А автор почему-то пихает туда float (xpos, ypos, etc..).
Так можно делать, или я чего-то не вижу, или автор косячит?
При приведении типа - дробная часть отбросится, конечно же если целая часть укладывается в int16_t. А если не укладывается - будет хрен пойми что. Так что, скорее всего, - косяк автора.
Причём заметь, он использует round() чтобы округлить до целого. Так что вообще не понятно нафига float.
определить как int16_t и этого будет достаточно?
И заодно здесь
void draw_hand_bold (int hand_angle, int hand_lenght_long, int hand_legth_short, int hand_dot_size)
тоже в int16_t, или нет смысла?
Компилятор не ругается, а вот с результатом есть сомнения..
Именно поэтому и “родился” этот вопрос.
Дело в том, что “стрелки”, если их сделать просто drawLine, на экране в некоторых позициях выглядят слишком уж “ломаными”.
Я понимаю, что на 128x160 экране (я использую 1.8’’ ST7735) особой “гладкости” ожидать не стОит, но..
В моём случае стрелка иногда выглядит из трёх смещённых линий..
Вот отсюда и сомнения, не есть ли причина этих “изломов” в некорректном коде..
А мне больше интересно - не сломает ли это (отмена округления) логику работы формулы. Ведь синусы и косинусы не целые значения (ну кроме 1 и -1) имеют ))))
Я вообще сейчас подумал, что автор применил float только потому, что ему было лень с кучей преобразования типов возиться в формуле. То есть таким образом просто «упростил себе жизнь»… )))
Проведи все эксперименты и расскажи потом:
Просто явное приведение типа к (int)/static_cast без округления.
Сходил перекурить и вот чтО надумалось в процессе :
Ведь собственно линию рисует функция билиотеки, в которую передаются всего лишь НАЧАЛЬНЫЕ и КОНЕЧНЫЕ координаты фигуры.
Так-что, вероятнее всего, “изломанность” живёт в либе.
И все мои шаманства эффекта не дадут..
// Функция для рисования жирной стрелки (например, минутной или часовой)
// Параметры:
// hand_angle - угол стрелки в градусах (0° = верх, 90° = право, по часовой стрелке)
// hand_lenght_long - длина длинной части стрелки (от центра до внешнего конца)
// hand_legth_short - длина короткой части стрелки (от центра до внутреннего конца)
// hand_dot_size - толщина/радиус стрелки в пикселях
void draw_hand_bold (int hand_angle, int hand_lenght_long, int hand_legth_short, int hand_dot_size) {
// Объявляем переменные для координат
float xpos; // X-координата внешнего конца стрелки (дальний от центра)
float ypos; // Y-координата внешнего конца стрелки
float xpos2; // X-координата внутреннего конца стрелки (ближний к центру)
float ypos2; // Y-координата внутреннего конца стрелки
// Смещения для создания толщины стрелки
float tri_xoff; // Смещение по X для создания "толщины" (перпендикулярно направлению стрелки)
float tri_yoff; // Смещение по Y для создания "толщины"
// ===========================================================================
// РАСЧЕТ КООРДИНАТ ОСНОВНЫХ ТОЧЕК СТРЕЛКИ
// ===========================================================================
// Преобразуем угол из градусов в радианы, так как sin/cos работают с радианами
// Рассчитываем координаты ВНЕШНЕГО конца стрелки:
xpos = round(center_x + sin(radians(hand_angle)) * hand_lenght_long);
// center_x - X-координата центра циферблата
// sin(radians(hand_angle)) - синус угла стрелки (определяет смещение по X)
// Умножаем на hand_lenght_long - получаем расстояние от центра по X
// round() - округляем до целого числа, так как экранные координаты дискретны
ypos = round(center_y - cos(radians(hand_angle)) * hand_lenght_long);
// center_y - Y-координата центра циферблата
// -cos(...) - минус потому что в экранных координатах Y увеличивается вниз,
// а в математической системе - вверх. Это инвертирует ось Y.
// cos(radians(hand_angle)) - косинус угла стрелки (определяет смещение по Y)
// Рассчитываем координаты ВНУТРЕННЕГО конца стрелки (ближе к центру):
xpos2 = round(center_x + sin(radians(hand_angle)) * hand_legth_short);
// Аналогично внешнему концу, но используем меньшую длину hand_legth_short
ypos2 = round(center_y - cos(radians(hand_angle)) * hand_legth_short);
// Аналогично внешнему концу, но используем меньшую длину
// ===========================================================================
// РАСЧЕТ СМЕЩЕНИЙ ДЛЯ СОЗДАНИЯ ТОЛЩИНЫ СТРЕЛКИ
// ===========================================================================
// Чтобы стрелка имела толщину, нам нужно сместиться на 90° от основного направления
// Это даст нам вектор, перпендикулярный направлению стрелки
tri_xoff = round(sin(radians(hand_angle + 90)) * hand_dot_size);
// hand_angle + 90 - поворачиваем на 90° для получения перпендикулярного направления
// sin(...) * hand_dot_size - рассчитываем смещение по X на величину толщины
tri_yoff = round(-cos(radians(hand_angle + 90)) * hand_dot_size);
// Аналогично для Y, с инверсией оси
// ===========================================================================
// ОТРИСОВКА СТРЕЛКИ
// ===========================================================================
// 1. Рисуем ОСНОВНУЮ ЛИНИЮ стрелки (от центра до внутреннего круга)
u8g2.drawLine(center_x, center_y, xpos2, ypos2);
// Рисует тонкую линию от центра до внутреннего круга стрелки
// 2. Рисуем два ЗАКРАШЕННЫХ КРУГА на концах стрелки
u8g2.drawDisc(xpos, ypos, hand_dot_size);
// Внешний круг (кончик стрелки) радиусом hand_dot_size
u8g2.drawDisc(xpos2, ypos2, hand_dot_size);
// Внутренний круг (ближе к центру) такого же радиуса
// ===========================================================================
// СОЗДАНИЕ "ТЕЛА" СТРЕЛКИ МЕЖДУ КРУГАМИ
// ===========================================================================
// Используем два треугольника для создания прямоугольника между кругами
// Это создает эффект толстой стрелки
// ПЕРВЫЙ треугольник:
u8g2.drawTriangle(
xpos + tri_xoff, // Верхняя правая точка внешнего конца (со смещением)
ypos + tri_yoff,
xpos - tri_xoff, // Нижняя левая точка внешнего конца (противоположное смещение)
ypos - tri_yoff,
xpos2 + tri_xoff, // Верхняя правая точка внутреннего конца
ypos2 + tri_yoff
);
// Этот треугольник покрывает правую половину прямоугольника между кругами
// ВТОРОЙ треугольник:
u8g2.drawTriangle(
xpos2 + tri_xoff, // Верхняя правая точка внутреннего конца
ypos2 + tri_yoff,
xpos2 - tri_xoff, // Нижняя левая точка внутреннего конца
ypos2 - tri_yoff,
xpos - tri_xoff, // Нижняя левая точка внешнего конца
ypos - tri_yoff
);
// Этот треугольник покрывает левую половину прямоугольника между кругами
// Вместе два треугольника образуют полный прямоугольник, соединяющий оба круга
}
Покрутите линейку по листочку клетчатой бумаги, у вас сразу появится понимание об отрисовке линий на дискретной сетке пикселей. Заодно полюбопыствтуйте на тему алгоритмов сглаживания (antialiasing). Может быть добудете, может быть напишете свою линию с субпиксельной точностью (и со сглаживанием).
А потом у вас начнутся окружности малых радиусов, что тоже вызовет бурю эмоций…
Так это ж бубль-гум - “выжимка” из того самого мною “стыренного” проекта !
Вот отсюда. С теми же “предполагаемыми косяками”.
Там есть ещё и шикарное видео на ютубе, с превосходными комментами.
Но.. На “буржуйском”.. Даже с русскими субтитрами тяжко воспринимается - уж очень шибко автор тараторит.
Так-что, за комменты на родном Могучем - Спасибо!!
Я правильно вас понимаю, что моё предположение -
вернО?
Неее.. Это уже не в этой жизни..
Мне хотя бы достичь хоть какого-то жиденького понимания всего этого..
Гм.. А вот с ними, что удивительно, никаких “эмоций”.. Тут как-то всё “вписалось в рамочки”.
В плане - глаз не режет.. Вероятно, как раз из-за малых радиусов. Либо по причине “старческих диоптрий”.
P.S. Кстати.. Пересмотрел внимательно упомянутое выше видео и..
Обнаружил те же самые “ступеньки” на секундной стрелке. На тех же самых “углах”..
Вообщем, float-ы всё же попробую убрать, но так - чисто из-за любви к искусству.