В функцию вместо ожидаемого int16_t передаётся float

Разве так можно?
Например, решил использовать в своём проекте (указатель азимута) подходящий код, где необходимо “рисовать стрелки”.
Нагуглил код “стрелочные часы”.
Вот кусочек кода рисования стрелок оттуда:

// 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.

если компилятор не ругается - значит можно.
Насколько результат получается в итоге правильным - это другой вопрос.

Т.е. переменные

float xpos;
  float ypos;
  float xpos2;
  float ypos2;  

  float tri_xoff;
  float tri_yoff;

определить как 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) особой “гладкости” ожидать не стОит, но..
В моём случае стрелка иногда выглядит из трёх смещённых линий..
Вот отсюда и сомнения, не есть ли причина этих “изломов” в некорректном коде..

Думаю да.

Тип int и так в ардуино 2х байтовый, так что без разницы

Сильно сомневаюсь.
Такое поведение из-за округления, скорее всего.

Может тогда вместо round применить приведение к int ?
Вот так

xpos = (int)(center_x + sin(radians(hand_angle)) * hand_lenght_long);
... etc...

чтобы вместо 64.89933, например, в ф-ю отдавать не 65, а 64.

Попробуй…
Только через static_cast, наверное, лучше…

Ок. Попоробую.
Только вот ещё что подумалось - а не изменит ли это всего лишь “направление излома”?
Т.е. вместо вниз, кверху?

Лан, что гадать. Доберусь до девайса, проверю. :slight_smile:

А мне больше интересно - не сломает ли это (отмена округления) логику работы формулы. Ведь синусы и косинусы не целые значения (ну кроме 1 и -1) имеют ))))

Я вообще сейчас подумал, что автор применил float только потому, что ему было лень с кучей преобразования типов возиться в формуле. То есть таким образом просто «упростил себе жизнь»… )))

Проведи все эксперименты и расскажи потом:

  1. Просто явное приведение типа к (int)/static_cast без округления.
  2. Приведение типа к int после округления
  3. Etc…

Сходил перекурить :slight_smile: и вот чтО надумалось в процессе :
Ведь собственно линию рисует функция билиотеки, в которую передаются всего лишь НАЧАЛЬНЫЕ и КОНЕЧНЫЕ координаты фигуры.
Так-что, вероятнее всего, “изломанность” живёт в либе.
И все мои шаманства эффекта не дадут..

Но всё же проверить надо. :wink:

1 лайк
// Функция для рисования жирной стрелки (например, минутной или часовой)
// Параметры:
//   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). Может быть добудете, может быть напишете свою линию с субпиксельной точностью (и со сглаживанием).

А потом у вас начнутся окружности малых радиусов, что тоже вызовет бурю эмоций…

1 лайк

Так это ж бубль-гум - “выжимка” из того самого мною “стыренного” проекта ! :slight_smile:
Вот отсюда. С теми же “предполагаемыми косяками”.
Там есть ещё и шикарное видео на ютубе, с превосходными комментами.
Но.. На “буржуйском”.. Даже с русскими субтитрами тяжко воспринимается - уж очень шибко автор тараторит. :slight_smile:
Так-что, за комменты на родном Могучем - Спасибо!!

Я правильно вас понимаю, что моё предположение -

вернО?

Неее.. Это уже не в этой жизни.. :slight_smile:
Мне хотя бы достичь хоть какого-то жиденького понимания всего этого..

Гм.. А вот с ними, что удивительно, никаких “эмоций”.. Тут как-то всё “вписалось в рамочки”.
В плане - глаз не режет.. Вероятно, как раз из-за малых радиусов. Либо по причине “старческих диоптрий”. :slight_smile:

P.S. Кстати.. Пересмотрел внимательно упомянутое выше видео и..
Обнаружил те же самые “ступеньки” на секундной стрелке. На тех же самых “углах”..
Вообщем, float-ы всё же попробую убрать, но так - чисто из-за любви к искусству. :slight_smile:

А как же без них?

Ха! Это разве ступеньки!
Вот какие “надо”!

Возьми дисплей с бОльшим разрешением и ступеньки станут плавнее…

а если выводить фото стрелок на дисплей, то скорее всего еще плавнее будет!)))

Схренали?

так этож фото, там криво точно нечего не должно быть, знай выводи себе и поворачивай чуть чуть)))