portaldacalheta.pt
  • Principal
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
Back-End

Comment obtenir des formes de coins arrondis en C ++ à l'aide de courbes de Bézier et de QPainter: un guide pas à pas



introduction

La tendance actuelle dans la conception graphique consiste à utiliser beaucoup de coins arrondis dans toutes sortes de formes. Nous pouvons observer ce fait sur de nombreuses pages Web, appareils mobiles et applications de bureau. Les exemples les plus notables sont les boutons poussoirs de l'application, qui sont utilisés pour déclencher une action lorsque l'utilisateur clique dessus. Au lieu d'une forme strictement rectangulaire avec des angles de 90 degrés dans les coins, ils sont souvent dessinés avec des coins arrondis. Les coins arrondis rendent l'interface utilisateur plus fluide et plus agréable. Je n'en suis pas entièrement convaincu, mais mon ami designer me le dit.

Les coins arrondis rendent l



Les coins arrondis rendent l'interface utilisateur plus fluide et plus agréable. Tweet

Les éléments visuels des interfaces utilisateur sont créés par les concepteurs, et le programmeur n'a qu'à les placer aux bons endroits. Mais que se passe-t-il lorsque nous devons générer une forme avec des coins arrondis à la volée et que nous ne pouvons pas la précharger? Certaines bibliothèques de programmation offrent des capacités limitées pour créer des formes prédéfinies avec des coins arrondis, mais elles ne peuvent généralement pas être utilisées dans des cas plus complexes. Par exemple, Cadre Qt a une classe QPainter , qui est utilisé pour dessiner sur toutes les classes dérivées de QPaintDevice, y compris les widgets, les pixmaps et les images. Il a une méthode appelée drawRoundedRect, qui, comme son nom l'indique, dessine un rectangle avec des coins arrondis. Mais si nous avons besoin d'une forme un peu plus complexe, nous devons la mettre en œuvre nous-mêmes. Comment pourrions-nous faire cela avec un polygone, une forme plane délimitée par un groupe de segments de ligne droite? Si nous avons un polygone dessiné avec un crayon sur un morceau de papier, ma première idée serait d'utiliser une gomme et de supprimer une petite partie des lignes à chaque coin, puis de connecter les extrémités du segment restant avec un arc de cercle. L'ensemble du processus peut être illustré dans la figure ci-dessous.



Comment créer manuellement des coins arrondis



tutoriel api node js rest

Classe QPainter a des méthodes surchargées nommées drawArc, qui peuvent dessiner des arcs circulaires. Tous nécessitent des paramètres qui définissent le centre et la taille de l'arc, l'angle de départ et la longueur de l'arc. S'il est facile de déterminer les valeurs nécessaires de ces paramètres pour un rectangle sans rotation, c'est une question entièrement différente lorsque nous avons affaire à des polygones plus complexes. De plus, nous devrions répéter ce calcul pour chaque sommet de polygone. Ce calcul est une tâche longue et fastidieuse, et les humains sont sujets à toutes sortes d'erreurs de calcul dans le processus. Cependant, c’est le travail des développeurs de logiciels de faire fonctionner les ordinateurs pour les êtres humains, et non l’inverse. Donc, ici, je vais montrer comment développer une classe simple, qui peut transformer un polygone complexe en une forme avec des coins arrondis. Les utilisateurs de cette classe n'auront qu'à ajouter des sommets de polygones, et la classe fera le reste. L'outil mathématique essentiel que j'utilise pour cette tâche est le Courbe de Bézier .

Courbes de Bézier

Il existe de nombreux livres de mathématiques et ressources Internet décrivant la théorie des courbes de Bézier, je vais donc brièvement décrire les propriétés pertinentes.



Par définition, la courbe de Bézier est une courbe entre deux points sur une surface bidimensionnelle dont la trajectoire est régie par un ou plusieurs points de contrôle. À proprement parler, une courbe entre deux points sans points de contrôle supplémentaires est également une courbe de Bézier. Cependant, comme cela se traduit par une ligne droite entre les deux points, ce n'est ni particulièrement intéressant, ni utile.

Courbes de Bézier quadratiques

Les courbes de Bézier quadratiques ont un point de contrôle. La théorie dit qu'une courbe de Bézier quadratique entre les points P0 et P2 avec point de contrôle Pun est défini comme suit:



B (t) = (1 - t)2P0+ 2t (1 - t) Pun+ t2P2, où 0 ≤ t ≤ 1 (un)

Donc quand t est égal à 0 , B (t) cédera P0 , quand t est égal à un , B (t) cédera P2 , mais dans tous les autres cas, la valeur de B (t) dépendra aussi de Pun . Depuis l'expression 2 t (1 - t) a une valeur maximale à t = 0,5 , c’est là que l’influence de Pun sur B (t) sera le plus grand. On peut penser à Pun comme d'une source imaginaire de gravité, qui tire la trajectoire de la fonction vers elle-même. La figure ci-dessous montre quelques exemples de courbes de Bézier quadratiques avec leurs points de départ, d'arrivée et de contrôle.



Courbes de Bézier quadratiques

Alors, comment résoudre notre problème en utilisant les courbes de Bézier? La figure ci-dessous offre une explication.



Comment créer des coins arrondis à l

Si nous imaginons supprimer un sommet de polygone et une courte partie de segments de ligne connectés dans son environnement, nous pouvons penser à une extrémité de segment de ligne à partir de P0 , l'autre segment de ligne se termine le P2 et le sommet supprimé à partir de Pun . Nous appliquons une courbe de Bézier quadratique à cet ensemble de points et voilà, il y a le coin arrondi souhaité.



Implémentation C ++ / Qt avec QPainter

Classe QPainter ne permet pas de dessiner des courbes de Bézier quadratiques. Bien qu'il soit assez facile de l'implémenter à partir de zéro en suivant l'équation (1), la bibliothèque Qt offre une meilleure solution. Il existe une autre classe puissante pour le dessin 2D: QPainterPath. Classe QPainterPath est une collection de lignes et de courbes qui peuvent être ajoutées et utilisées plus tard avec le QPainter objet. Il existe des méthodes surchargées qui ajoutent des courbes de Bézier à une collection actuelle. En particulier, les méthodes quadTo ajoutera une courbe de Bézier quadratique. La courbe commencera au courant QPainterPath point ( P0 ), tandis que Pun et P2 doivent être passés à quadTo comme paramètres.

QPainter Méthode de drawPath est utilisé pour dessiner une collection de lignes et de courbes à partir de QPainterPath objet, qui doit être donné en paramètre, avec stylet et pinceau actifs.

Voyons donc la déclaration de 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; };

J'ai décidé de sous-classer QPolygon de sorte que je n'ai pas à implémenter l'ajout de sommets et d'autres choses par moi-même. Outre le constructeur, qui définit simplement le rayon sur une valeur initiale raisonnable, cette classe a deux autres méthodes publiques:

  • SetRadius met le rayon à une valeur donnée. Le rayon est la longueur d'une ligne droite (en pixels) près de chaque sommet, qui sera supprimée (ou, plus précisément, non dessinée) pour le coin arrondi.
  • GetPath est l'endroit où tous les calculs ont lieu. Il renverra le QPainterPath objet généré à partir des points de polygone ajoutés à RoundedPolygon.

Les méthodes de la partie privée ne sont que des méthodes auxiliaires utilisées par GetPath.

Voyons la mise en œuvre et je commencerai par les méthodes privées:

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); }

Pas grand chose à expliquer ici, la méthode renvoie la distance euclidienne entre les deux points donnés.

php convertir utf8 en ascii
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éthode GetLineStart calcule l'emplacement du point P2 à partir de la dernière figure, si les points sont ajoutés au polygone dans le sens des aiguilles d'une montre. Plus précisément, il renverra un point, qui est m_uiRadius pixels de i -ème sommet dans la direction vers le (i+1) -ème sommet. Lors de l'accès au (i+1) -ème sommet, il faut se rappeler que dans le polygone, il y a aussi un segment de ligne entre le dernier et le premier sommet, ce qui en fait une forme fermée, d'où l'expression (i+1)%count() . Cela empêche également la méthode de sortir de la plage et accède au premier point à la place. Variable fRat contient le rapport entre le rayon et la longueur du segment de ligne i -ième. Il existe également un contrôle qui empêche fRat d'avoir une valeur sur 0.5. Si fRat avait une valeur supérieure à 0.5, les deux coins arrondis consécutifs se chevaucheraient, ce qui entraînerait un mauvais résultat visuel.

En voyageant depuis un point Pun à P2 en ligne droite et en parcourant 30% de la distance, nous pouvons déterminer notre emplacement à l'aide de la formule 0,7 • Pun+ 0,3 • P2 . En général, si nous atteignons une fraction de la distance totale, et α = 1 indique la distance totale, l'emplacement actuel est à (1 - α) • P1 + α • P2 .

C'est ainsi que GetLineStart méthode détermine l'emplacement du point qui est m_uiRadius pixels de i -th sommet dans la direction de (i+1) -th.

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; }

Cette méthode est très similaire à GetLineStart. Il calcule l'emplacement du point P0 pour le (i+1) -th sommet, pas i -th. En d'autres termes, si on trace une ligne à partir de GetLineStart(i) à GetLineEnd(i) pour chaque i entre 0 et n-1, où n est le nombre de sommets du polygone, nous obtiendrions le polygone avec des sommets effacés et leur environnement proche.

Et maintenant, la méthode de classe principale:

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; }

Dans cette méthode, nous construisons le QPainterPath objet. Si le polygone n'a pas au moins trois sommets, nous n'avons plus affaire à une forme 2D, et dans ce cas, la méthode émet un avertissement et renvoie le chemin vide. Lorsque suffisamment de points sont disponibles, nous bouclons sur tous les segments de droite du polygone (le nombre de segments de ligne est, bien sûr, égal au nombre de sommets), en calculant le début et la fin de chaque segment de droite entre les arrondis coins. Nous mettons une ligne droite entre ces deux points et une courbe de Bézier quadratique entre la fin du segment de ligne précédent et le début du courant, en utilisant l'emplacement du sommet courant comme point de contrôle. Après la boucle, nous devons fermer le chemin avec une courbe de Bézier entre le dernier et le premier segments de ligne car dans la boucle, nous avons tracé une ligne droite de plus que les courbes de Bézier.

Classe RoundedPolygon utilisation et résultats

Il est maintenant temps de voir comment utiliser ce cours dans la pratique.

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');

Ce morceau de code source est assez simple. Après avoir initialisé deux QPixmaps et leur QPainters, nous créons un RoundedPolygon objet et remplissez-le de points. Peintre P1 dessine le polygone régulier, tandis que P2 dessine le QPainterPath avec des coins arrondis, générés à partir du polygone. Les deux pixmaps résultants sont enregistrés dans leurs fichiers et les résultats sont les suivants:

Coins arrondis à l

Conclusion

Nous avons vu que générer une forme avec des coins arrondis à partir d'un polygone n'est pas si difficile après tout, surtout si nous utilisons un bon cadre de programmation tel que Qt. Ce processus peut être automatisé par la classe que j'ai décrite dans ce blog comme preuve de concept. Cependant, il reste encore beaucoup à faire, comme:

  • Faites des coins arrondis uniquement aux sommets sélectionnés et pas du tout.
  • Faites des coins arrondis avec différents rayons à différents sommets.
  • Implémentez une méthode qui génère une polyligne avec des coins arrondis (la polyligne dans la terminologie Qt est exactement comme un polygone, sauf qu'il ne s'agit pas d'une forme fermée car il manque le segment de ligne entre le dernier et le premier sommet).
  • Utilisez RoundedPolygon pour générer des bitmaps, qui peuvent être utilisés comme masque de widget d'arrière-plan pour produire des widgets de forme folle.
  • Le RoundedPolygon la classe n'est pas optimisée pour la vitesse d'exécution; Je l'ai laissé tel quel pour une meilleure compréhension du concept. L'optimisation peut inclure le calcul de nombreuses valeurs intermédiaires lors de l'ajout d'un nouveau sommet au polygone. Aussi, quand GetPath est sur le point de renvoyer une référence au QPainterPath généré, il pourrait définir un indicateur, indiquant que l'objet est à jour. Le prochain appel à GetPath aurait pour résultat de ne renvoyer que le même QPainterPath objet, sans rien recalculer. Le développeur devrait cependant s'assurer que cet indicateur est effacé à chaque changement dans l'un des sommets du polygone, ainsi qu'à chaque nouveau sommet, ce qui me fait penser que la classe optimisée serait mieux développée à partir de zéro et non dérivée à partir de QPolygon. La bonne nouvelle, c'est que ce n'est pas aussi difficile qu'il y paraît.

Au total, le RoundedPolygon La classe, telle quelle, peut être utilisée comme un outil chaque fois que nous voulons ajouter un touche design à notre interface graphique à la volée, sans préparer à l'avance des pixmaps ou des formes.

En relation: Comment apprendre les langages C et C ++: la liste ultime

Un didacticiel sur les FusionCharts détaillés dans jQuery

Science Des Données Et Bases De Données

Un didacticiel sur les FusionCharts détaillés dans jQuery
Développement de projet IA - Comment les chefs de projet doivent se préparer

Développement de projet IA - Comment les chefs de projet doivent se préparer

Les Tendances

Articles Populaires
Explorer la fonctionnalité Get & Transform d'Excel
Explorer la fonctionnalité Get & Transform d'Excel
Comment aborder les wrappers pour les propriétés Swift
Comment aborder les wrappers pour les propriétés Swift
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
ApeeScape s'associe à Guidant Global pour offrir un accès à la demande au réseau Elite de pigistes
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Tutoriel Apache Spark Streaming: Identifier les hashtags Twitter tendances
Conception accessible vs conception inclusive (avec infographie)
Conception accessible vs conception inclusive (avec infographie)
 
L'intégration continue d'iOS avec le serveur Xcode expliquée
L'intégration continue d'iOS avec le serveur Xcode expliquée
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Meilleurs éditeurs de programmation? Une bataille sans fin sans vainqueur clair
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Comment GWT déverrouille la réalité augmentée dans votre navigateur
Webpack ou Browserify & Gulp: quel est le meilleur?
Webpack ou Browserify & Gulp: quel est le meilleur?
Business Analyst - Stratégie & Analytics
Business Analyst - Stratégie & Analytics
Articles Populaires
  • défis rencontrés par les développeurs Java
  • une police peut consister en une taille de caractère, un style de caractère et
  • comment collecter des données sur twitter
  • javascript typeerror n'est pas une fonction
  • un moyen utile de représenter visuellement les données
Catégories
  • Science Des Données Et Bases De Données
  • Conception Mobile
  • Design De Marque
  • Personnes Et Équipes Produit
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt