apeescape2.com
  • メイン
  • Uiデザイン
  • その他
  • エンジニアリング管理
  • バックエンド
バックエンド

ベジェ曲線とQPainterを使用してC ++で丸みを帯びた角の形状を取得する方法:ステップバイステップガイド

前書き

ザ・ グラフィックデザインの現在の傾向 あらゆる形の丸い角をたくさん使うことです。この事実は、多くのWebページ、モバイルデバイス、およびデスクトップアプリケーションで確認できます。最も注目すべき例は、クリックされたときに何らかのアクションをトリガーするために使用されるアプリケーションのプッシュボタンです。角が90度の厳密な長方形ではなく、角が丸い形で描かれることがよくあります。丸みを帯びた角は、ユーザーインターフェイスをよりスムーズで快適に感じさせます。私はこれについて完全に確信しているわけではありませんが、私のデザイナーの友人は私にそう言っています。

角が丸いので、ユーザーインターフェイスがよりスムーズで快適になります

丸みを帯びた角は、ユーザーインターフェイスをよりスムーズで快適に感じさせます。 つぶやき

ユーザーインターフェイスの視覚的要素はデザイナーによって作成され、プログラマーはそれらを適切な場所に配置するだけで済みます。しかし、その場で角が丸い形状を生成する必要があり、それをプリロードできない場合はどうなりますか?一部のプログラミングライブラリは、角が丸い事前定義された形状を作成するための機能が制限されていますが、通常、より複雑なケースでは使用できません。例えば、 Qtフレームワーク クラスがあります QPainter 、ウィジェット、ピックスマップ、画像など、QPaintDeviceから派生したすべてのクラスを描画するために使用されます。 drawRoundedRectというメソッドがあり、名前が示すように、角が丸い長方形を描画します。しかし、もう少し複雑な形状が必要な場合は、自分で実装する必要があります。直線セグメントのグループで囲まれた平面形状であるポリゴンを使用して、これをどのように行うことができますか?紙に鉛筆でポリゴンを描いた場合、最初のアイデアは、消しゴムを使用して各コーナーの線の小さな部分を削除し、残りのセグメントの端を円弧で接続することです。プロセス全体を次の図に示します。



ユニットテストを行う方法

丸みを帯びた角を手動で作成する方法

クラスQPainter drawArcという名前のオーバーロードされたメソッドがいくつかあり、円弧を描くことができます。それらはすべて、円弧の中心とサイズ、開始角度、および円弧の長さを定義するパラメータを必要とします。回転していない長方形に必要なこれらのパラメータの値を決定するのは簡単ですが、より複雑なポリゴンを扱う場合はまったく別の問題です。さらに、すべてのポリゴン頂点に対してこの計算を繰り返す必要があります。この計算は時間のかかる面倒な作業であり、人間はその過程であらゆる種類の計算エラーを起こしがちです。ただし、コンピュータを人間のために機能させるのはソフトウェア開発者の仕事であり、その逆ではありません。そこで、ここでは、複雑なポリゴンを角の丸い形に変えることができる単純なクラスを開発する方法を示します。このクラスのユーザーは、ポリゴンの頂点を追加するだけでよく、残りはクラスが行います。このタスクに使用する基本的な数学ツールは、 ベジェ曲線 。

ベジェ曲線

ベジェ曲線の理論を説明する数学の本やインターネットリソースがたくさんあるので、関連するプロパティの概要を簡単に説明します。

定義上、ベジェ曲線は2次元サーフェス上の2点間の曲線であり、その軌道は1つ以上の制御点によって制御されます。厳密に言えば、追加の制御点がない2点間の曲線もベジェ曲線です。ただし、これにより2点が直線になるため、特に興味深いものでも、有用なものでもありません。

二次ベジェ曲線

二次ベジェ曲線には1つの制御点があります。理論によれば、点間の2次ベジェ曲線 P0 そして P2 コントロールポイント付き P1 次のように定義されます。

B(t)=(1-t)2P0+ 2t(1-t)P1+ t2P2、ここで、0≤t≤1 (1)

そうするとき t に等しい 0 、 B(t) 降伏します P0 、 いつ t に等しい 1 、 B(t) 降伏します P2 、しかし他のすべての場合、の値 B(t) に依存します P1 。式以来 2t(1-t) で最大値を持っています t = 0.5 、それは P1 オン B(t) 最高になります。私たちは考えることができます P1 関数の軌道をそれ自体に引き寄せる架空の重力源として。次の図は、開始点、終了点、および制御点を持つ2次ベジェ曲線のいくつかの例を示しています。

二次ベジェ曲線

では、ベジェ曲線を使用して問題をどのように解決するのでしょうか?下の図は説明を提供します。

コードを使用して丸みを帯びた角を作成する方法

ポリゴンの頂点とその周囲の接続された線分の短い部分を削除することを想像すると、1つの線分の終わりは次のように考えることができます。 P0 、他の線分は現在終了しています P2 および削除された頂点 P1 。この点と出来上がりのセットに2次ベジェ曲線を適用します。目的の丸い角があります。

QPainterを使用したC ++ / Qtの実装

クラスQPainter二次ベジェ曲線を描く方法はありません。式(1)に従って最初から実装するのは非常に簡単ですが、Qtライブラリはより優れたソリューションを提供します。 2D描画には別の強力なクラスがあります:QPainterPath。クラスQPainterPathは、後で追加してQPainterで使用できる線と曲線のコレクションです。オブジェクト。現在のコレクションにベジェ曲線を追加するオーバーロードされたメソッドがいくつかあります。特に、メソッドquadTo二次ベジェ曲線を追加します。曲線は現在のQPainterPathから始まりますポイント( P0 )、一方 P1 そして P2 quadToに渡す必要がありますパラメータとして。

QPainterの方法drawPath QPainterPathから線と曲線のコレクションを描画するために使用されますアクティブなペンとブラシを使用して、パラメータとして指定する必要があるオブジェクト。

それでは、クラス宣言を見てみましょう。

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

サブクラス化することにしましたQPolygonそのため、頂点やその他のものを自分で追加する必要はありません。半径を適切な初期値に設定するコンストラクターの他に、このクラスには2つのパブリックメソッドがあります。

  • SetRadiusメソッドは、半径を指定された値に設定します。半径は、各頂点の近くの直線の長さ(ピクセル単位)であり、丸みを帯びた角では削除されます(より正確には描画されません)。
  • GetPathすべての計算が行われる場所です。 QPainterPathを返しますRoundedPolygonに追加されたポリゴンポイントから生成されたオブジェクト。

プライベート部分のメソッドは、GetPathで使用される単なる補助メソッドです。

javascriptノードとは

実装を見てみましょう。プライベートメソッドから始めます。

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

ここで説明することはあまりありませんが、このメソッドは、指定された2点間のユークリッド距離を返します。

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

メソッドGetLineStartポイントの位置を計算します P2 最後の図から、ポイントが時計回りにポリゴンに追加された場合。より正確には、m_uiRadiusであるポイントを返します。 i番目の頂点から(i+1)番目の頂点に向かう方向のピクセル。 (i+1)番目の頂点にアクセスするとき、ポリゴンでは、最後の頂点と最初の頂点の間に線分もあり、閉じた形状になるため、式(i+1)%count()を覚えておく必要があります。 。これにより、メソッドが範囲外になるのを防ぎ、代わりに最初のポイントにアクセスします。変数fRat半径とi番目の線分の長さの比率を保持します。 fRatを防ぐチェックもあります0.5を超える値を持つことから。 fRatの場合の値が0.5を超えると、2つの連続する丸い角が重なり、視覚的な結果が低下します。

ポイントから旅行するとき P1 に P2 直線で、距離の30%を完了することにより、次の式を使用して位置を特定できます。 0.7•P1+ 0.3•P2 。一般に、全距離の一部を達成した場合、 α= 1 は完全な距離を示し、現在の場所は (1-α)•P1 +α•P2 。

これがGetLineStartの方法ですメソッドは、m_uiRadiusであるポイントの位置を決定しますi -th頂点から(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; }

この方法はGetLineStartと非常によく似ています。ポイントの位置を計算します P0 (i+1) -thではなく、i -th頂点の場合。言い換えれば、GetLineStart(i)から線を引くと〜GetLineEnd(i) iごとに0の間およびn-1、ここでnはポリゴン内の頂点の数です。頂点とその周辺が消去されたポリゴンを取得します。

そして今、メインクラスのメソッド:

レスポンシブウェブデザイン画像のベストプラクティス
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; }

この方法では、QPainterPathを作成しますオブジェクト。ポリゴンに少なくとも3つの頂点がない場合、2D形状を処理しなくなります。この場合、メソッドは警告を発行し、空のパスを返します。十分なポイントが利用可能になったら、ポリゴンのすべての直線セグメントをループし(もちろん、線セグメントの数は頂点の数と同じです)、丸められた間の各直線セグメントの開始と終了を計算します。コーナー。現在の頂点の位置を制御点として使用して、これら2つの点の間に直線を置き、前の線分の終わりと電流の始まりの間に2次ベジェ曲線を置きます。ループの後、最後の線分と最初の線分の間のベジェ曲線でパスを閉じる必要があります。これは、ループではベジェ曲線よりも1本多く直線を描いたためです。

クラスRoundedPolygon使用法と結果

それでは、このクラスを実際に使用する方法を見てみましょう。

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

このソースコードは非常に単純です。 2つを初期化した後QPixmapsそしてそれらのQPainters、RoundedPolygonを作成しますオブジェクトを作成し、ポイントで埋めます。画家P1 P2が正多角形を描画している間、 QPainterPathを描画しますポリゴンから生成された、角が丸い。結果の両方のピックスマップがファイルに保存され、結果は次のようになります。

QPainterを使用した丸い角

結論

特にQtなどの優れたプログラミングフレームワークを使用している場合、ポリゴンから角が丸い形状を生成することは、結局のところそれほど難しくないことがわかりました。このプロセスは、概念実証としてこのブログで説明したクラスによって自動化できます。ただし、次のような改善の余地はまだたくさんあります。

  • 選択した頂点でのみ角を丸くし、すべての頂点では作成しません。
  • さまざまな頂点でさまざまな半径の丸い角を作成します。
  • 角が丸いポリラインを生成するメソッドを実装します(Qt用語のポリラインは、最後の頂点と最初の頂点の間の線分が欠落しているため閉じた形状ではないことを除いて、ポリゴンと同じです)。
  • RoundedPolygonを使用するビットマップを生成します。これを背景ウィジェットマスクとして使用して、クレイジーな形のウィジェットを作成できます。
  • RoundedPolygonクラスは実行速度に対して最適化されていません。コンセプトをわかりやすくするためにそのままにしておきました。最適化には、ポリゴンに新しい頂点を追加するときに多くの中間値を計算することが含まれる場合があります。また、GetPathの場合生成されたQPainterPathへの参照を返そうとしている場合、オブジェクトが最新であることを示すフラグを設定できます。 GetPathへの次の呼び出し同じQPainterPathのみを返すことになりますオブジェクト、何も再計算せずに。ただし、開発者は、ポリゴンの頂点が変更されるたびに、また新しい頂点ごとにこのフラグがクリアされるようにする必要があります。これにより、最適化されたクラスは最初から開発され、派生しない方がよいと思います。 QPolygonから。幸いなことに、これは思ったほど難しくはありません。

全体として、RoundedPolygonクラスは、そのまま、追加したいときにいつでもツールとして使用できます。 デザイナータッチ 事前にピックスマップやシェイプを準備しなくても、その場でGUIにアクセスできます。

関連: CおよびC ++言語を学ぶ方法:究極のリスト

フリーランス開発者向けのホスティング:PaaS、VPS、クラウドなど

バックエンド

フリーランス開発者向けのホスティング:PaaS、VPS、クラウドなど
分離不安:Linux名前空間でシステムを分離するためのチュートリアル

分離不安:Linux名前空間でシステムを分離するためのチュートリアル

技術

人気の投稿
マークダウンを学ぶ:ソフトウェア開発者のためのライティングツール
マークダウンを学ぶ:ソフトウェア開発者のためのライティングツール
美徳シグナリングの芸術:なぜこれほど多くのブランドがそれを間違えるのか
美徳シグナリングの芸術:なぜこれほど多くのブランドがそれを間違えるのか
価格弾力性2.0:理論から現実世界へ
価格弾力性2.0:理論から現実世界へ
ApeeScapeとFacebook-グローバル仮想オフィスの作成
ApeeScapeとFacebook-グローバル仮想オフィスの作成
サイバーセキュリティ:すべてのCEOとCFOが知っておくべきこと
サイバーセキュリティ:すべてのCEOとCFOが知っておくべきこと
 
WebRTCアプリケーションを構築する1年:スタートアップエンジニアリングの教訓
WebRTCアプリケーションを構築する1年:スタートアップエンジニアリングの教訓
PHP 7の概要:新機能と廃止点
PHP 7の概要:新機能と廃止点
ラベルなしデータを使用した半教師あり画像分類
ラベルなしデータを使用した半教師あり画像分類
GWTがブラウザの拡張現実を解き放つ方法
GWTがブラウザの拡張現実を解き放つ方法
UXスケッチについて知っておくべきことすべて
UXスケッチについて知っておくべきことすべて
人気の投稿
  • c法人vss法人税
  • C ++を学ぶための最速の方法
  • デザインのさまざまな例の原則
  • コールオプションの値
  • なぜ建築家は自分の建物にシンプルで反復的なリズムを選ぶのでしょうか?
  • ar vs vr vs mr
カテゴリー
人とチーム 収益と成長 トレンド 製品ライフサイクル リモートの台頭 技術 モバイル 設計プロセス 収益性と効率性 その他

© 2021 | 全著作権所有

apeescape2.com