Помогите с рисованием дуги.

sirota
Offline
Зарегистрирован: 18.08.2015

Помогите найти реализованные алгоритмы на C рисования дуги по входным параметрам - координаты центра, радиус, угол отклонения, угол дуги. Я уже вымотался... все что у меня выходит - это точки:

int DegToRad(int Deg) {
  return Deg * 180 * PI;
}

void AngleArc(int CenterX, int CenterY, int Radius, float StartAngle, float SweepAngle, uint16_t color) {
  float X;
  float Y;
  for (float Pos = 0; Pos <= SweepAngle; Pos += 0.1) {
    float F = SweepAngle * Pos / SweepAngle - SweepAngle / 2 + 180 + StartAngle;
    X = sin(DegToRad(F));
    Y = cos(DegToRad(F));
    tft.drawPixel((int) (CenterX + X * Radius), (int) (CenterY +  Y * Radius), color);
  }
}

И то дорисовывает 4 маленьких дуги. Да и по точкам не ахти. Ни где не могу достать исходников AngleArc. В делфе он меня устраивает, но выдернуть исходники ни как не выходит. А ноутбук на которм должны лежать исходники 31 декабря пал смертью храбрых... даже корпус поведен (

заранее спасибо.

sirota
Offline
Зарегистрирован: 18.08.2015

Если что рисую на tft экране с уже довольно сильно модифицированной ADAFRUIT_GFX. Хотя возможен переезд на другую либу с поддержкой ili9341 подключенной напрямую без i2c и прочих.

Logik
Offline
Зарегистрирован: 05.08.2014

А точки говорите выходят?! Так проводите прямые от предыдущей точки к текущей.

sirota
Offline
Зарегистрирован: 18.08.2015

Думал уже. Но в условиях скажем avr когда приходится пользоваться только целыми выходит что скажем если на окружность (пример) с радиусом в 25 пикселей 360 вычислей норма, то окружность с радиусом в 100 писелей выгляджит очень не гуманно. Да еще и в условиях округления некоторые точки выстреливают дальше визульного радиуса окружности где-то на 3-4 пикселя и это я делал для окружности с радиусом в 50 пикселей.

Logik
Offline
Зарегистрирован: 05.08.2014

Вістреливают-плохо. Посмотрите в ADAFRUIT_GFX как строится окружность, по идее это это - https://ru.wikipedia.org/wiki/Алгоритм_Брезенхэма . Там в общем рисуется четверть круга, остальное симметрично зеркалируется. Если добавить перед прорисовкой точки проверку принадлежности точки дуге - получим что надо.

sirota
Offline
Зарегистрирован: 18.08.2015

Применен алгоритм Брезенхэма для построения окружностей. Может строить как дугу, так естественно и окружность полностью.

Координатная сетка:

сверху - вниз - Y от 0 на увеличение

слева - направо - X от 0 на увеличение

Т.е. левый верхний угол (0, 0)

Тут 4 вызова:

AngleArc(100, 100, 100, 0, 45, RED);
AngleArc(100, 100, 100, 90, 45, GREEN);
AngleArc(100, 100, 100, 180, 45, BLUE);
AngleArc(100, 100, 100, 270, 45, YELLOW);

тут 1 вызов:

AngleArc(100, 100, 100, 90, 300, RED);

Как видим - процедура может рисовать не тоько от 0 и на 360, но и от любого угла на любой угол (естественно каждый угол ограничен 360).

Т.е. в данном примере отрисовка начинается с 90 градусов на 300 градусов. Т.е. дорисовывает до 360 270 градусов и с 0 градусов дорисовывает оставшиеся 30.

Если входной начальный градус больше 360, он приравнивается 0.

Если угол поворота больше 360, он приравнивается 360.

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

Собственно сам код:

int16_t get_angle(int16_t centerX, int16_t centerY, int16_t pointX, int16_t pointY){
  int16_t x = pointX - centerX; 
  int16_t y = pointY - centerY; 
  if(x==0) return (y>0) ? 180 : 0; 
  int16_t a = atan2(y, x)*180/PI; 
  a += 90;
  if ((x < 0) && (y < 0)) a+=360;
  return a; 
}


void AngleArc(int16_t _x, int16_t _y, int16_t r, int16_t startAngle, int16_t sweepAngle, uint16_t color) {
  startAngle = abs(startAngle);
  sweepAngle = abs(sweepAngle);
  if (startAngle >= 360) startAngle = 0;
  if (sweepAngle > 360) sweepAngle = 360;
  if (startAngle + sweepAngle > 360) {
    AngleArc(_x, _y, r, 0, startAngle + sweepAngle - 360, color);
    sweepAngle = 360 - startAngle;
  }
  int16_t x = 0;
  int16_t y = r;
  int16_t gap = 0;
  int16_t delta = (2 - 2 * r);
  int16_t angle;
  while (y >= 0) {
    
    angle = get_angle(_x, _y, _x + x, _y - y);
    if ((angle >= startAngle) && (angle <= startAngle + sweepAngle)) {
      if (0<=angle<=90) tft.drawPixel(_x + x, _y - y, color);
    }
    
    angle = get_angle(_x, _y, _x + x, _y + y);
    if ((angle >= startAngle) && (angle <= startAngle + sweepAngle)) {
      if (90<angle<=180) tft.drawPixel(_x + x, _y + y, color);
    }

    angle = get_angle(_x, _y, _x - x, _y + y);
    if ((angle >= startAngle) && (angle <= startAngle + sweepAngle)) {
      if (180<angle<=270) tft.drawPixel(_x - x, _y + y, color);
    }

    angle = get_angle(_x, _y, _x - x, _y - y);
    if ((angle >= startAngle) && (angle <= startAngle + sweepAngle)) {
      if (270<angle<=360) tft.drawPixel(_x - x, _y - y, color);
    }

    gap = 2 * (delta + y) - 1;
    if (delta < 0 && gap <= 0) {
      x++;
      delta += 2 * x + 1;
      continue;
    }
    if (delta > 0 && gap > 0) {
      y--;
      delta -= 2 * y + 1;
      continue;
    }
    x++;
    delta += 2 * (x - y);
    y--;
  }
}

Спасибо Logik за то что ткнул все таки носом. Если у кого-то есть предложения по оптимизации, буду рад услышать.

James
Offline
Зарегистрирован: 26.02.2016

есть кто? вопрос с толщиной линий, как в этой либе их сделать толще?

sirota
Offline
Зарегистрирован: 18.08.2015

James пишет:

есть кто? вопрос с толщиной линий, как в этой либе их сделать толще?

Рисуется тут пиксельно. Могу предложить заменить tft.drawPixel(_x + x, _y - y, color); на рисованием прямой нужной длины (длина прямо = ширине) с центром в центре круга.

James
Offline
Зарегистрирован: 26.02.2016

мне нужно 2 прямоугольника:( что-то типо эквалайзера. с помощью какого класса реализовать?

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Есть библиотека на C - ugui. Можно её использовать в качестве примера.

sirota
Offline
Зарегистрирован: 18.08.2015

James пишет:

мне нужно 2 прямоугольника:( что-то типо эквалайзера. с помощью какого класса реализовать?

Что Вам нужно? Вам нужна окружность или прямоугольник? Если окружность определенного радиуса, то чем-то да помогу, если прямоугольник... ту вы ошиблись темой и не срите тут по пусту, а создайте свою тему. Спасибо. 

James
Offline
Зарегистрирован: 26.02.2016

спасибо поковыряю.