C#+WPFチューニング戦記

C#とWPFで高速なコードと最適なシステムを書くためにやってきたいろいろな事を書いてみます。.NET Frameworkのソースコードを読み解きましょう。なお、ここに書かれているのは個人の見解であって何らかの団体や企業の見解を代表するものではありません。

ベジェ曲線のHitTest

WPFにあまり任せてはいけないのがベジェ曲線のHitTestです。
几帳面で抜け目ありませんが、きわめて低速です。

真面目に方程式を解く手法ももちろんあるのですが、実はあんまり工夫しないでも、そこそこに優良なコードは書けます。
概ねこんなノリです。動かしてないので間違っていたらすみません。

/// <summary>
/// 矩形とベジェの交差判定
/// </summary>
/// <param name="v">Vecter[4]で、始点、制御点1、制御点2、終点</param>
/// <param name="rect">交差しているか判定したい範囲。スタックに山積みさせないために ref 。面積が0は不可</param>
/// <returns>矩形とヒットしている場合true</returns>
/// <remarks></remarks>
bool HitTestBezier(Vector[] v, ref Rect rect)
{
    var p0 = new Point(v[0].X, v[0].Y);
    var p3 = new Point(v[3].X, v[3].Y);
    // 始点か終点を含んでいれば、Hitしている。
    if (rect.Contains(p0) || rect.Contains(p3))
        return true;

    var area = new Rect(p0, p3);
    area.Union(new Rect(new Point(v[1].X,v[1].Y),new Point(v[2].X,v[2].Y)));

    // ここらへんで小細工の余地はあります。幅や高さが1未満だったら直線と大差ないとか、いろいろ。

    // 交差している可能性があるケースを判定
    if(rect.IntersectsWith(area))
    {
        var v01 = (v[0] + v[1]) / 2;
        var v12 = (v[1] + v[2]) / 2;
        var v23 = (v[2] + v[3]) / 2;

        var v0112 = (v01 + v12) / 2;
        var v1223 = (v12 + v23) / 2;

        var c = (v0112 + v1223) / 2;

        // 2分割してさらに探査
        return HitTestBezier(new[] { v[0], v01, v0112, c }, ref rect)
            || HitTestBezier(new[] { c, v1223, v23, v[3] }, ref rect);
    }
    // 可能性が無い
    return false;
}

とりあえず、WPFに丸任せするよりは高速です。お試しください。
長さが20億ピクセルくらいあるベジェ曲線でも最悪32段ほど潜ればちゃんと交差判定できます。小細工をすればもっと浅くできます。
これで、画面内に有るか無いか仮想化に役立てようという考えです。