2016-11-23 16 views
1

QTのポイントセットで滑らかな線を描く方法はありますか? ポイントの数と位置は実行時に設定されます。QTのいくつかの点を通過する滑らかな曲線を描くには?

現在、私はQPainterPathを描画します。このQPainterPathには、ポイントツーポイントのlineToを含むパスが作成されます。レンダリングヒントのアンチエイリアスを使用しますが、パスはまだギザギザです。

QSplineSeriesはこのような曲線的なパスを与えるようですが、私が使用しているQtバージョンであるQt4.8では使用できません。

もう1つのオプションは、ベジエ曲線を使用していますが、1つの開始点と終了点と2つの制御点を使用するため、すべてのセグメント(すべてのLineTo)で計算し、現時点では持っていない。

答えて

1

私は基本的に2つの接続された線を取り、それらの間の接続点を取り除き、それを曲線に置き換える何らかの回避策を実装しました。このような変更が目に見えない小さな線がたくさんあるので、私は非常に短いすべての線を取り除き、開いた端を再接続します。 https://www.toptal.com/c-plus-plus/rounded-corners-bezier-curves-qpainter

そして、ここでの機能:機能は主にボヤンKverhによって提供された、彼のチュートリアルをチェックアウト

namespace 
{ 
    float distance(const QPointF& pt1, const QPointF& pt2) 
    { 
     float hd = (pt1.x() - pt2.x()) * (pt1.x() - pt2.x()); 
     float vd = (pt1.y() - pt2.y()) * (pt1.y() - pt2.y()); 
     return std::sqrt(hd + vd); 
    } 

    QPointF getLineStart(const QPointF& pt1, const QPointF& pt2) 
    { 
     QPointF pt; 
     float rat = 10.0/distance(pt1, pt2); 
     if (rat > 0.5) { 
      rat = 0.5; 
     } 
     pt.setX((1.0 - rat) * pt1.x() + rat * pt2.x()); 
     pt.setY((1.0 - rat) * pt1.y() + rat * pt2.y()); 
     return pt; 
    } 

    QPointF getLineEnd(const QPointF& pt1, const QPointF& pt2) 
    { 
     QPointF pt; 
     float rat = 10.0/distance(pt1, pt2); 
     if (rat > 0.5) { 
      rat = 0.5; 
     } 
     pt.setX(rat * pt1.x() + (1.0 - rat)*pt2.x()); 
     pt.setY(rat * pt1.y() + (1.0 - rat)*pt2.y()); 
     return pt; 
    } 

} 

void PainterPath::smoothOut(const float& factor) 
{ 
    QList<QPointF> points; 
    QPointF p; 
    for (int i = 0; i < mPath->elementCount() - 1; i++) { 
     p = QPointF(mPath->elementAt(i).x, mPath->elementAt(i).y); 

     // Except for first and last points, check what the distance between two 
     // points is and if its less then min, don't add them to the list. 
     if (points.count() > 1 && (i < mPath->elementCount() - 2) && (distance(points.last(), p) < factor)) { 
      continue; 
     } 
     points.append(p); 
    } 

    // Don't proceed if we only have 3 or less points. 
    if (points.count() < 3) { 
     return; 
    } 

    QPointF pt1; 
    QPointF pt2; 
    QPainterPath* path = new QPainterPath(); 
    for (int i = 0; i < points.count() - 1; i++) { 
     pt1 = getLineStart(points[i], points[i + 1]); 
     if (i == 0) { 
      path->moveTo(pt1); 
     } else { 
      path->quadTo(points[i], pt1); 
     } 
     pt2 = getLineEnd(points[i], points[i + 1]); 
     path->lineTo(pt2); 
    } 

    delete mPath; 
    mPath = path; 
    prepareGeometryChange(); 
} 
0

QSplineSeriesがQt 5.xの機能であることにお気づきのように、Qt 4.8にはすぐに使えるソリューションはないとは思いません。またQSplineSeriesQtChartsモジュールの一部です(QtDataVisualizationのように)商用ライセンスを持っていない、またはあなたのプロジェクトがGPLでない限り、それを使用することはできません。

あなたはそれを必要とする数学を経て自分で実装する必要があります(または素晴らしい実装(C++であってもQt互換である必要はありません))。

ベジェ曲線に言及しているので、composite Bezier curveにショットを与えることをお勧めします。私が取り組んでいるプロジェクトのためにそのことを実装したことを覚えています。それはいくつかの仕事が必要でした。 :D This articleをお使いになると助かります。

ベジェ曲線は、実際にはBスプライン(正しく覚えていれば)です。特に滑らかさの欠如で解決できる場合は、合成ベジェ曲線をかなり高速に生成できます。彼らの頑強さと人気に私はあなたがまともな実装をオンラインで見つけることができると確信しています。たぶんQtにはやらないかもしれませんが、正しく書かれていれば、すぐにコードを修正できるはずです。

This非常に有望です(ActionScriptにありますが、mehです)。または、曲線の計算に必要な2つのコントロールポイントを指定することができれば、ベジェ曲線を作成できるショットをQPainterPath::cubicTo()に与えることもできます。

+0

ええ、私はそれに簡単な解決策がないことを恐れていたが、私は2番目を持っていますベジェ曲線とBスプラインを見てください。 –

+0

悪いニュースには申し訳ありません。私は非常に便利なリンクで回答を更新しました(実装にはActionScriptを使用します)。 – rbaleksandar

+0

ありがとう、それは面倒な面白そうに見えます、私はそれを試してみましょう:D –

0

かなり多くの人がこのタスクに3次補間を使用しています。選択肢は、ベジェ曲線またはCatmull-Romスプラインです。すべての点に当てなければならない場合は、「ハンドル」またはベジエのコントロールポイント間の線を真っ直ぐに保つ必要があります。次に、最小二乗法を使ってフィットさせます。

Catmullロムスプラインは、2つの特別なコントロールポイント(開始点と終了点、それを作成するための単純なミラーポイント)が必要であるという利点があります。ポイントが合理的に滑らかである限り、ラインは正常に動作します。 QTグラフィックスがCatMull Romスプラインを直接描画することはまずありません。標準的な公開メソッドであるBeziersに変換すると、Catmull RomからBezierに簡単に移動できますが、逆もありません。すべてのベジエがCatmull Romで表現できるわけではありません。ほんの数点。

立方体があなたの望むカーブを与えない場合は、他の補間法、eq五次関数を使用することができます。

関連する問題