portaldacalheta.pt
  • Principal
  • Mundo
  • Europa
  • Ciência De Dados E Bancos De Dados
  • Gerenciamento De Projetos
Processo Interno

Como obter formas de canto arredondado em C ++ usando curvas de Bezier e QPainter: um guia passo a passo



Introdução

o tendência atual em design gráfico é usar muitos cantos arredondados em todos os tipos de formatos. Podemos observar esse fato em muitas páginas da web, dispositivos móveis e aplicativos de desktop. Os exemplos mais notáveis ​​são os botões de ação do aplicativo, que são usados ​​para acionar alguma ação quando clicados. Em vez da forma estritamente retangular com ângulos de 90 graus nos cantos, eles geralmente são desenhados com cantos arredondados. Os cantos arredondados tornam a interface do usuário mais suave e agradável. Não estou totalmente convencido disso, mas meu amigo designer me diz isso.

Cantos arredondados tornam a interface do usuário mais suave e agradável



arquivos de cabeçalho c ++
Os cantos arredondados tornam a interface do usuário mais suave e agradável. Tweet

Os elementos visuais das interfaces de usuário são criados por designers, e o programador só precisa colocá-los nos lugares certos. Mas o que acontece, quando temos que gerar uma forma com cantos arredondados instantaneamente e não podemos pré-carregá-la? Algumas bibliotecas de programação oferecem recursos limitados para a criação de formas predefinidas com cantos arredondados, mas geralmente, elas não podem ser usadas em casos mais complicados. Por exemplo, Framework Qt tem uma aula QPainter , que é usado para desenhar em todas as classes derivadas de QPaintDevice, incluindo widgets, pixmaps e imagens. Possui um método denominado drawRoundedRect, que, assim como o nome sugere, desenha um retângulo com cantos arredondados. Mas se precisarmos de uma forma um pouco mais complexa, temos que implementá-la nós mesmos. Como poderíamos fazer isso com um polígono, uma forma plana limitada por um grupo de segmentos de linha reta? Se tivermos um polígono desenhado com um lápis em um pedaço de papel, minha primeira ideia seria usar uma borracha e deletar uma pequena parte das linhas em cada canto e então conectar as pontas restantes do segmento com um arco circular. Todo o processo pode ser ilustrado na figura abaixo.



Como criar cantos arredondados manualmente



Classe QPainter possui alguns métodos sobrecarregados chamados drawArc, que podem desenhar arcos circulares. Todos eles requerem parâmetros, que definem o centro e o tamanho do arco, o ângulo inicial e o comprimento do arco. Embora seja fácil determinar os valores necessários desses parâmetros para um retângulo não girado, é uma questão totalmente diferente quando lidamos com polígonos mais complexos. Além disso, teríamos que repetir esse cálculo para cada vértice do polígono. Esse cálculo é uma tarefa longa e cansativa, e os humanos estão sujeitos a todos os tipos de erros de cálculo no processo. No entanto, é trabalho dos desenvolvedores de software fazer com que os computadores funcionem para os seres humanos, e não vice-versa. Então, aqui vou mostrar como desenvolver uma classe simples, que pode transformar um polígono complexo em uma forma com cantos arredondados. Os usuários desta classe terão apenas que anexar vértices poligonais e a classe fará o resto. A ferramenta matemática essencial que utilizo para esta tarefa é a Curva de Bezier .

Curvas de Bézier

Existem muitos livros de matemática e recursos da Internet que descrevem a teoria das curvas de Bezier, portanto, irei delinear brevemente as propriedades relevantes.



Por definição, a curva de Bézier é uma curva entre dois pontos em uma superfície bidimensional, cuja trajetória é governada por um ou mais pontos de controle. A rigor, uma curva entre dois pontos sem pontos de controle adicionais também é uma curva de Bézier. No entanto, como isso resulta em uma linha reta entre os dois pontos, não é particularmente interessante, nem útil.

Curvas quadráticas de Bézier

As curvas quadráticas de Bezier têm um ponto de controle. A teoria diz que uma curva de Bézier quadrática entre pontos P0 e P2 com ponto de controle P1 é definido como segue:



B (t) = (1 - t)2P0+ 2t (1 - t) P1+ t2P2, onde 0 ≤ t ≤ 1 (1)

Então quando t é igual a 0 , B (t) vai render P0 , quando t é igual a 1 , B (t) vai render P2 , mas em todos os outros casos, o valor de B (t) também dependerá de P1 . Desde a expressão 2t (1 - t) tem um valor máximo em t = 0,5 , é aí que a influência de P1 em B (t) será o maior. Podemos pensar em P1 como de uma fonte imaginária de gravidade, que puxa a trajetória da função em sua direção. A figura abaixo mostra alguns exemplos de curvas quadráticas de Bezier com seus pontos inicial, final e de controle.



Curvas quadráticas de Bézier

scorp vs c corp vs llc

Então, como resolvemos nosso problema usando as curvas de Bézier? A figura abaixo oferece uma explicação.



Como criar cantos arredondados usando o código

Se imaginarmos deletar um vértice do polígono e uma pequena parte dos segmentos de linha conectados em seus arredores, podemos pensar em uma extremidade de segmento de linha a partir de P0 , o outro segmento de linha termina a partir de P2 e o vértice excluído a partir de P1 . Aplicamos uma curva de Bézier quadrática a este conjunto de pontos e voila, lá está o canto arredondado desejado.



Implementação C ++ / Qt usando QPainter

Classe QPainter não tem como desenhar curvas quadráticas de Bézier. Embora seja muito fácil implementá-lo do zero seguindo a equação (1), a biblioteca Qt oferece uma solução melhor. Há outra classe poderosa para desenho 2D: QPainterPath. Classe QPainterPath é uma coleção de linhas e curvas que podem ser adicionadas e usadas posteriormente com QPainter objeto. Existem alguns métodos sobrecarregados que adicionam curvas de Bézier a uma coleção atual. Em particular, os métodos quadTo irá adicionar curvas de Bézier quadráticas. A curva começará no atual QPainterPath ponto ( P0 ), enquanto P1 e P2 deve ser passado para quadTo como parâmetros.

QPainter Método de drawPath é usado para desenhar uma coleção de linhas e curvas de QPainterPath objeto, que deve ser dado como parâmetro, com caneta e pincel ativos.

ajuste de desempenho do sql server 2014

Então, vamos ver a declaração da classe:

class RoundedPolygon : public QPolygon { public: RoundedPolygon() { SetRadius(10); } void SetRadius(unsigned int iRadius) { m_iRadius = iRadius; } const QPainterPath& GetPath(); private: QPointF GetLineStart(int i) const; QPointF GetLineEnd(int i) const; float GetDistance(QPoint pt1, QPoint pt2) const; private: QPainterPath m_path; unsigned int m_iRadius; };

Decidi criar uma subclasse QPolygon para que eu não precise implementar a adição de vértices e outras coisas sozinho. Além do construtor, que apenas define o raio para algum valor inicial sensível, esta classe tem dois outros métodos públicos:

  • SetRadius método define o raio para um determinado valor. Raio é o comprimento de uma linha reta (em pixels) próxima a cada vértice, que será excluída (ou, mais precisamente, não desenhada) para o canto arredondado.
  • GetPath é onde todos os cálculos ocorrem. Ele retornará o QPainterPath objeto gerado a partir dos pontos do polígono adicionados a RoundedPolygon.

Os métodos da parte privada são apenas métodos auxiliares usados ​​por GetPath.

tela @media e (largura máxima

Vamos ver a implementação e começarei com os métodos privados:

float RoundedPolygon::GetDistance(QPoint pt1, QPoint pt2) const { float fD = (pt1.x() - pt2.x())*(pt1.x() - pt2.x()) + (pt1.y() - pt2.y()) * (pt1.y() - pt2.y()); return sqrtf(fD); }

Não há muito o que explicar aqui, o método retorna a distância euclidiana entre os dois pontos dados.

QPointF RoundedPolygon::GetLineStart(int i) const { QPointF pt; QPoint pt1 = at(i); QPoint pt2 = at((i+1) % count()); float fRat = m_uiRadius / GetDistance(pt1, pt2); if (fRat > 0.5f) fRat = 0.5f; pt.setX((1.0f-fRat)*pt1.x() + fRat*pt2.x()); pt.setY((1.0f-fRat)*pt1.y() + fRat*pt2.y()); return pt; }

Método GetLineStart calcula a localização do ponto P2 da última figura, se os pontos forem adicionados ao polígono no sentido horário. Mais precisamente, ele retornará um ponto, que é m_uiRadius pixels de i -ésimo vértice na direção do (i+1) -ésimo vértice. Ao acessar o (i+1) -ésimo vértice, devemos lembrar que no polígono também existe um segmento de reta entre o último e o primeiro vértice, o que o torna uma forma fechada, daí a expressão (i+1)%count() . Isso também evita que o método saia do alcance e acesse o primeiro ponto. Variável fRat mantém a proporção entre o raio e o i -ésimo comprimento do segmento de linha. Também há uma verificação que impede fRat de ter um valor acima de 0.5. Se fRat tivesse um valor acima de 0.5, então os dois cantos arredondados consecutivos se sobreporiam, o que causaria um resultado visual ruim.

Ao viajar de um ponto P1 para P2 em uma linha reta e completando 30 por cento da distância, podemos determinar nossa localização usando a fórmula 0,7 • P1+ 0,3 • P2 . Em geral, se atingirmos uma fração da distância total, e α = 1 denota a distância total, a localização atual é em (1 - α) • P1 + α • P2 .

É assim que o GetLineStart método determina a localização do ponto que é m_uiRadius pixels de i -ésimo vértice na direção de (i+1) -ésimo.

QPointF RoundedPolygon::GetLineEnd(int i) const { QPointF pt; QPoint pt1 = at(i); QPoint pt2 = at((i+1) % count()); float fRat = m_uiRadius / GetDistance(pt1, pt2); if (fRat > 0.5f) fRat = 0.5f; pt.setX(fRat*pt1.x() + (1.0f - fRat)*pt2.x()); pt.setY(fRat*pt1.y() + (1.0f - fRat)*pt2.y()); return pt; }

Este método é muito semelhante a GetLineStart. Calcula a localização do ponto P0 para o (i+1) -ésimo vértice, não i -ésimo. Em outras palavras, se desenharmos uma linha de GetLineStart(i) para GetLineEnd(i) para cada i entre 0 e n-1, onde n é o número de vértices no polígono, obteríamos o polígono com vértices apagados e seus arredores próximos.

E agora, o método principal da classe:

é uma empresa considerada uma sociedade unipessoal
const QPainterPath& RoundedPolygon::GetPath() { m_path = QPainterPath(); if (count() <3) { qWarning() << 'Polygon should have at least 3 points!'; return m_path; } QPointF pt1; QPointF pt2; for (int i = 0; i < count(); i++) { pt1 = GetLineStart(i); if (i == 0) m_path.moveTo(pt1); else m_path.quadTo(at(i), pt1); pt2 = GetLineEnd(i); m_path.lineTo(pt2); } // close the last corner pt1 = GetLineStart(0); m_path.quadTo(at(0), pt1); return m_path; }

Neste método, construímos o QPainterPath objeto. Se o polígono não possui pelo menos três vértices, não estamos mais lidando com uma forma 2D e, neste caso, o método emite um aviso e retorna o caminho vazio. Quando pontos suficientes estão disponíveis, fazemos um loop sobre todos os segmentos de linha reta do polígono (o número de segmentos de linha é, obviamente, igual ao número de vértices), calculando o início e o fim de cada segmento de linha reta entre os cantos. Colocamos uma linha reta entre esses dois pontos e uma curva Bézier quadrática entre o final do segmento de linha anterior e o início da corrente, usando a localização do vértice atual como ponto de controle. Após o loop, temos que fechar o caminho com uma curva de Bézier entre o último e o primeiro segmento de linha, porque no loop desenhamos uma linha reta a mais do que as curvas de Bezier.

Classe RoundedPolygon uso e resultados

Agora é hora de ver como usar essa aula na prática.

QPixmap pix1(300, 200); QPixmap pix2(300, 200); pix1.fill(Qt::white); pix2.fill(Qt::white); QPainter P1(&pix1); QPainter P2(&pix2); P1.setRenderHints(QPainter::Antialiasing); P2.setRenderHints(QPainter::Antialiasing); P1.setPen(QPen(Qt::blue, 2)); P1.setBrush(Qt::red); P2.setPen(QPen(Qt::blue, 2)); P2.setBrush(Qt::red); RoundedPolygon poly; poly << QPoint(147, 187) << QPoint(95, 187) << QPoint(100, 175) << QPoint(145, 165) << QPoint(140, 95) << QPoint(5, 85) << QPoint(5, 70) << QPoint(140, 70) << QPoint(135, 45) << QPoint(138, 25) << QPoint(145, 5) << QPoint(155, 5) << QPoint(162, 25) << QPoint(165, 45) << QPoint(160, 70) << QPoint(295, 70) << QPoint(295, 85) << QPoint(160, 95) << QPoint(155, 165) << QPoint(200, 175) << QPoint(205, 187) << QPoint(153, 187) << QPoint(150, 199); P1.drawPolygon(poly); P2.drawPath(poly.GetPath()); pix1.save('1.png'); pix2.save('2.png');

Este pedaço de código-fonte é bastante simples. Após inicializar dois QPixmaps e seu QPainters, criamos um RoundedPolygon objeto e preencha-o com pontos. Pintor P1 desenha o polígono regular, enquanto P2 desenha o QPainterPath com cantos arredondados, gerados a partir do polígono. Ambos os pixmaps resultantes são salvos em seus arquivos e os resultados são os seguintes:

Cantos arredondados usando o QPainter

Conclusão

Vimos que gerar uma forma com cantos arredondados a partir de um polígono não é tão difícil afinal, especialmente se usarmos uma boa estrutura de programação como o Qt. Esse processo pode ser automatizado pela classe que descrevi neste blog como uma prova de conceito. No entanto, ainda há muito espaço para melhorias, como:

  • Faça cantos arredondados apenas nos vértices selecionados e não em todos eles.
  • Faça cantos arredondados com raios diferentes em vértices diferentes.
  • Implemente um método que gere uma polilinha com cantos arredondados (polilinha na terminologia Qt é como um polígono, exceto que não é uma forma fechada porque falta o segmento de linha entre o último e o primeiro vértice).
  • Use RoundedPolygon para gerar bitmaps, que podem ser utilizados como máscara de widget de fundo para produzir widgets de formas malucas.
  • O RoundedPolygon a classe não é otimizada para velocidade de execução; Deixei como está para facilitar a compreensão do conceito. A otimização pode incluir o cálculo de muitos valores intermediários ao anexar um novo vértice ao polígono. Além disso, quando GetPath está prestes a retornar uma referência ao QPainterPath gerado, ele pode definir um sinalizador, indicando que o objeto está atualizado. A próxima chamada para GetPath resultaria em apenas retornar o mesmo QPainterPath objeto, sem recalcular nada. O desenvolvedor teria, no entanto, que certificar-se de que este sinalizador seja limpo em cada mudança em qualquer um dos vértices do polígono, bem como em cada novo vértice, o que me faz pensar que a classe otimizada seria melhor desenvolvida do zero e não derivada de QPolygon. A boa notícia é que isso não é tão difícil quanto parece.

Ao todo, o RoundedPolygon classe, como está, pode ser usada como uma ferramenta sempre que quisermos adicionar um toque de designer para nossa GUI em tempo real, sem preparar pixmaps ou formas com antecedência.

Relacionado: Como aprender as linguagens C e C ++: a lista definitiva

Síria: Reino Unido sanciona aliados de Assad

Mundo

Síria: Reino Unido sanciona aliados de Assad
Fazendo seu software trabalhar para você: um tutorial de implementação de ERP

Fazendo seu software trabalhar para você: um tutorial de implementação de ERP

Processos Financeiros

Publicações Populares
A lista abrangente de conferências Agile
A lista abrangente de conferências Agile
Na noite antes da inauguração, Biden lidera luto pelas vítimas do vírus
Na noite antes da inauguração, Biden lidera luto pelas vítimas do vírus
Documentação do Algoritmo Ruby com AsciiDoc e Knitr
Documentação do Algoritmo Ruby com AsciiDoc e Knitr
Especialista em comunicação
Especialista em comunicação
Um guia de campo para DesignOps
Um guia de campo para DesignOps
 
Página inicial Smart IoT: domesticando a Internet das coisas
Página inicial Smart IoT: domesticando a Internet das coisas
Quanto tempo sua startup pode sobreviver sem um CFO em tempo integral?
Quanto tempo sua startup pode sobreviver sem um CFO em tempo integral?
Um Mergulho Profundo na Aprendizagem por Reforço
Um Mergulho Profundo na Aprendizagem por Reforço
As mulheres deverão usar o hijab, mas não a burca, disse o porta-voz do Talibã
As mulheres deverão usar o hijab, mas não a burca, disse o porta-voz do Talibã
Spring Security com JWT para REST API
Spring Security com JWT para REST API
Publicações Populares
  • quantas empresas usam o sharepoint
  • melhores práticas para design de interface
  • práticas recomendadas de design de página de destino
  • Calculadora de custos do empregador para compensação do empregado
  • na psicologia, os princípios da Gestalt são usados ​​para explicar
  • a comunicação se torna mais simples quando você aumenta o número de membros da equipe.
Categorias
  • Mundo
  • Europa
  • Ciência De Dados E Bancos De Dados
  • Gerenciamento De Projetos
  • © 2022 | Todos Os Direitos Reservados

    portaldacalheta.pt