dhq_boiler
@dhq_boiler

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

描画したジオメトリの閉領域をFillしたい

Q&A

Closed

解決したいこと

boiler's Graphics でフリーハンド描画ツール(仮称)を実装しました。これはPolyBezierSegmentクラスを利用して、描画時にドラッグした軌跡にそのまま曲線を描くツールとなっております。

poly_bezier.gif

このツールで描画したオブジェクトに対して、結合オプションのUnionを実行したときに、描画したジオメトリの閉領域をViewModelのFillColorで塗りつぶししたいのですが、うまくいかないのです。

2022-01-10.png

Union実施後
理想(ペイントで作成しました):

2022-01-10 - コピー.png

実際:

2022-01-10 (1).png

PolyBezier曲線を構成する点群はIEnumerable<Point>です。
私が試したことはこの点群コレクションで重複するPointがあったら、そこでトリミングするようにしたのですが、このツールで描画すると交点があるように見えて、実際はずれてるようなのです。つまり、片方の線の点1と点2による線ともう片方の線の点3と点4による線が重なっているので、点が重なっているわけではないということです。(ちょっと言い回しが難しい...。)なので、重複する点が存在せず、この方法ではやりたいことを実現できませんでした。

私が求めているのはGeometry.Combineメソッドのように、何か都合のよいメソッドがどこかにないかということです。誰か知っていたら教えて下さい。

GeometryCreator.cs
    public static class GeometryCreator
    {
        :
        public static PathGeometry CreateCombineGeometry(PolyBezierViewModel pb)
        {
            var geometry = new StreamGeometry();
            using (var ctx = geometry.Open())
            {
                ctx.BeginFigure(pb.Points[0], true, true);
                ctx.PolyBezierTo(pb.Points.Skip(1).ToList(), true, false);
            }
            geometry.Freeze();
            return PathGeometry.CreateFromGeometry(geometry);
        }
        :
    }

該当するソースコード

ブランチ:feature/PolyBezierSegment

0

2Answer

自己解決しました。

https://stackoverflow.com/questions/25563953/how-to-clip-non-closed-geometry
上記の記事を参考にコードを書いたらできました。

GeometryCreator.cs
        public static PathGeometry CreateCombineGeometry(PolyBezierViewModel pb)
        {
            Point oneIntersection;
            int beginI = 0;
            int endJ = 0;
            for (int i = 0; i < pb.Points.Count - 1; i++)
            {
                var pt1 = pb.Points[i];
                var pt2 = pb.Points[i + 1];
                for (int j = 0; j < pb.Points.Count - 1; j++)
                {
                    if (i == j || i + 1 == j || i == j + 1 || (i == endJ && j == beginI)) continue;
                    var pt3 = pb.Points[j];
                    var pt4 = pb.Points[j + 1];
                    if (Intersects(pt1, pt2, pt3, pt4, out var intersection))
                    {
                        oneIntersection = intersection;
                        beginI = i;
                        endJ = j;
                    }
                }
            }

            var geometry = new StreamGeometry();
            using (var ctx = geometry.Open())
            {
                ctx.BeginFigure(oneIntersection, true, true);
                ctx.PolyBezierTo(ExtractSegment(pb.Points, beginI + 1, endJ), true, false);
            }
            geometry.Freeze();
            return PathGeometry.CreateFromGeometry(geometry);
        }

        private static IList<Point> ExtractSegment(ObservableCollection<Point> points, int beginI, int endJ)
        {
            return points.Skip(beginI).Take(endJ - beginI).ToList();
        }

        static bool Intersects(Point a1, Point a2, Point b1, Point b2, out Point intersection)
        {
            intersection = new Point(0, 0);

            Vector b = a2 - a1;
            Vector d = b2 - b1;
            double bDotDPerp = b.X * d.Y - b.Y * d.X;

            if (bDotDPerp == 0)
                return false;

            Vector c = b1 - a1;
            double t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
            if (t < 0 || t > 1)
                return false;

            double u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
            if (u < 0 || u > 1)
                return false;

            intersection = a1 + t * b;

            return true;
        }

2022-01-10 (3).png

2022-01-10 (4).png

0Like

Your answer might help someone💌