Обсуждение статьи "Торговые инструменты на MQL5 (Часть 17): Изучение векторных скругленных прямоугольников и треугольников"

 

Опубликована статья Торговые инструменты на MQL5 (Часть 17): Изучение векторных скругленных прямоугольников и треугольников:

В этой статье мы рассматриваем векторные методы для рисования скругленных прямоугольников и треугольников в MQL5 с использованием canvas и суперсэмплирования для сглаживания изображения. Мы реализуем заливку методом сканирования строк, геометрические предварительные вычисления для дуг и касательных, а также рисование рамок для создания плавных, настраиваемых фигур. Такой подход закладывает основу для современных элементов пользовательского интерфейса в будущих торговых инструментах, поддерживающего входные параметры для установки размеров, радиусов, рамок и прозрачности.

Векторный подход к рендерингу скругленных прямоугольников и треугольников использует математические описания фигур — точки, линии и кривые — вместо пиксельных сеток, что позволяет создавать масштабируемые, независимые от разрешения графические изображения, сохраняющие четкость при любом размере. В отличие от растровых методов, которые при масштабировании могут создавать неровные края (зубчатость изображения), векторные методы вычисляют точные рамки и заливки, используя уравнения для дуг и касательных, что делает их идеальными для элементов пользовательского интерфейса в MQL5, где плавные визуальные эффекты повышают удобство использования без потери эффективности. Закругленные углы достигаются заменой острых вершин дугами окружности, радиусы которых определяют кривизну. Границы представляют собой смещенные контуры или утолщенные края, а суперсэмплинг дополнительно улучшает результат, выполняя рендеринг в более высоком разрешении с последующим понижением дискретизации для устранения артефактов.

Мы планируем реализовать объекты canvas высокого разрешения с помощью суперсэмплинга, предварительно вычислять геометрию дуг и касательных в треугольниках, применять заливку методом сканирования строк для обеих фигур, чтобы обеспечить точное отображение внутренних областей, а также добавлять настраиваемые рамки посредством векторных прямых линий и угловых дуг. Мы будем обрабатывать пользовательские входные параметры для указания размеров, радиусов, прозрачности и цветов в целях создания гибких, сглаженных фигур, подходящих для современных торговых интерфейсов. Вкратце, ниже представлено наглядное представление наших целей.


Автор: Allan Munene Mutiiria

 
Спасибо за эту статью! Она очень полезная и подробная.
Я пытаюсь добавить эффект скошенных краев (3D освещение - белое сверху/слева, черное снизу/ справа) к моему закругленному прямоугольнику.
Я успешно реализовал эффект скошенных краев на прямых краях, но у меня возникли проблемы с их соединением на углах.
Смотрите красные круги на изображении ниже, где линии пересекаются.
Есть предложения?


//+------------------------------------------------------------------+
//| СКОШЕННЫЕ КРАЯ ЭФЕКТОВ
//+------------------------------------------------------------------+
void DrawBeveledEdges(int posX, int posY, int width, int height, int radius, int scaleFactor)
  {
   color lightColor = clrWhite;
   uint lightARGB = ColorToARGBWithOpacity(lightColor, 20);

   color darkColor = clrBlack;
   uint darkARGB = ColorToARGBWithOpacity(darkColor, 35);

   int bevelWidth = 3 * scaleFactor;

// ===== СВЕТЛАЯ СТОРОНА (СЛЕВА ВВЕРХУ) =====

// Верхняя горизонтальная часть
   for(int y = 0; y < bevelWidth; y++)
     {
      for(int x = radius; x < width - radius; x++)
        {
         rectangleHighResCanvas.PixelSet(posX + x, posY + y, lightARGB);
        }
     }

// Левая вертикальная часть
   for(int x = 0; x < bevelWidth; x++)
     {
      for(int y = radius; y < height - radius; y++)
        {
         rectangleHighResCanvas.PixelSet(posX + x, posY + y, lightARGB);
        }
     }

// Левый верхний АРК - ТОЛЬКО светлый цвет
   for(int dy = -radius; dy <= 0; dy++)
     {
      for(int dx = -radius; dx <= 0; dx++)
        {
         double dist = MathSqrt((double)(dx*dx + dy*dy));

         if(dist >= radius - bevelWidth && dist <= radius + 1)
           {
            int pixX = posX + radius + dx;
            int pixY = posY + radius + dy;

            if(pixX >= posX && pixY >= posY)
               rectangleHighResCanvas.PixelSet(pixX, pixY, lightARGB);
           }
        }
     }

// Правый верхний угол - ТОЛЬКО светлый цвет (правый верхний угол)
   for(int dy = -radius; dy <= 0; dy++)
     {
      for(int dx = 0; dx <= radius; dx++)
        {
         double dist = MathSqrt((double)(dx*dx + dy*dy));

         if(dist >= radius - bevelWidth && dist <= radius + 1)
           {
            int pixX = posX + width - radius + dx;
            int pixY = posY + radius + dy;

            if(pixX < posX + width && pixY >= posY)
               rectangleHighResCanvas.PixelSet(pixX, pixY, lightARGB);
           }
        }
     }

// ===== ТЕМНЫЙ КРАЙ (СПРАВА ВНИЗУ) =====

// Apakšējā horizontālā daļa
   for(int y = height - bevelWidth; y < height; y++)
     {
      for(int x = radius; x < width - radius; x++)
        {
         rectangleHighResCanvas.PixelSet(posX + x, posY + y, darkARGB);
        }
     }

// Правая вертикальная часть
   for(int x = width - bevelWidth; x < width; x++)
     {
      for(int y = radius; y < height - radius; y++)
        {
         rectangleHighResCanvas.PixelSet(posX + x, posY + y, darkARGB);
        }
     }

// Правый нижний угол ARC - ТОЛЬКО темный цвет
   for(int dy = 0; dy <= radius; dy++)
     {
      for(int dx = 0; dx <= radius; dx++)
        {
         double dist = MathSqrt((double)(dx*dx + dy*dy));

         if(dist >= radius - bevelWidth && dist <= radius + 1)
           {
            int pixX = posX + width - radius + dx;
            int pixY = posY + height - radius + dy;

            if(pixX < posX + width && pixY < posY + height)
               rectangleHighResCanvas.PixelSet(pixX, pixY, darkARGB);
           }
        }
     }

// Левый нижний ARC - ТОЛЬКО темный цвет (левый нижний угол)
   for(int dy = 0; dy <= radius; dy++)
     {
      for(int dx = -radius; dx <= 0; dx++)
        {
         double dist = MathSqrt((double)(dx*dx + dy*dy));

         if(dist >= radius - bevelWidth && dist <= radius + 1)
           {
            int pixX = posX + radius + dx;
            int pixY = posY + height - radius + dy;

            if(pixX >= posX && pixY < posY + height)
               rectangleHighResCanvas.PixelSet(pixX, pixY, darkARGB);
           }
        }
     }
  }
//+------------------------------------------------------------------+


P.S. - Идеи статей на будущее:
Эффекты тени и свечения - как добавить глубину элементам пользовательского интерфейса
Градиентные заливки - создание 3D-вида с помощью цветовых переходов
Техника сглаживания - сглаживание краев для профессионального вида
Внутренние тени - усиление 3D-эффектов со скошенными краями
Текстура и шумовые узоры - добавление реализма к плоским формам