4点和椭圆

时间:2011-03-27 19:04:43

标签: c# wpf silverlight silverlight-4.0 drawing

我有4分..我可以绘制一个多边形使用此代码

var p = new Polygon();
p.Points.Add(new Point(0, 0));
p.Points.Add(new Point(70, 0));
p.Points.Add(new Point(90, 100));
p.Points.Add(new Point(0, 80));

如何使用Silverlight绘制适合此多边形的'椭圆'?

enter image description here

问题仍然没有发现!!!

4 个答案:

答案 0 :(得分:9)

一种方法是使用QuadraticBezierSegmentBezierSegment

例如,像这样:

    <Path Stroke="Red" StrokeThickness="2" >
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,40">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                <BezierSegment Point1="0,93"
                                           Point2="90,117"
                                           Point3="80,50"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
<Path Stroke="Red" StrokeThickness="2" >
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,40">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                <BezierSegment Point1="0,-13"
                                           Point2="70,-17"
                                           Point3="80,50"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
<Polygon Points="0,0 70,0 90,100 0,80"></Polygon>

it look like this

这不是确切的解决方案,因为确切地说,你必须计算曲线的精确点并使用4 QuadraticBezierSegment

修改 QuadraticBezierSegment

的示例
<Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,40">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="6,79"
                                           Point2="45,90"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
    <Path Stroke="Red" StrokeThickness="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure StartPoint="45,90">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="80,91"
                                           Point2="80,50"
                                           />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
    <Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,40">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="2,3"
                                           Point2="35,0"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
    <Path Stroke="Red" StrokeThickness="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure StartPoint="35,0">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="72,8"
                                           Point2="80,50"
                                           />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
    <Polygon Name="pol"  Points="0,0 70,0 90,100 0,80" Stroke="Red" StrokeThickness="1"</Polygon>

this is image of this example

它仍然是一个实验性的,而不是计算点,但它非常精确。

<强> EDIT2: 您可以使用此图像和我的评论来计算曲线点:

enter image description here

曲线具有起点,中间点和终点。在此图像中,开始和结束点是L,M,N,O;和midle是W,X,Y,Z

例如,我们如何计算点L

在行y = k * x + b的等式的帮助下,我们找到了行AB,DC,AC,DB,AD的等式。我们找到ACDB的交叉点R。我们找到ABDC的交叉点E。之后,我们找到了行ER的等式以及我们找到的ERAD的交叉点L

我们如何计算点W

长度为l = sqrt(sqr(x2 - x1) + sqr(y2 - y1))的等式的帮助找到AR的长度。 AW = AR/(4*pi)和这个系数的帮助,以及线的方程和长度方程,在求解方程后,我们找到W

我们发现类似的其他观点。

此算法不仅适用于具有平行线的多边形,但在这种情况下算法更容易。长度系数是相同的。

在这个算法的帮助下,我找到了你的例子的3条曲线的点:

<Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,36">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="4.7,74.6"
                                           Point2="39.9,88.9"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
    <Path Stroke="Red" StrokeThickness="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure StartPoint="39.9,88.9">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="83.43,92.7"
                                           Point2="78.8,43.9"
                                           />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
    <Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                    <PathFigure StartPoint="0,36">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment Point1="3.55,3.94"
                                           Point2="31.8,0"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>

它的曲线与椭圆线完全相同。图片如下:

enter image description here

您可以将此算法翻译为“公式”,以便找到您的解决方案。

你需要4个功能:

1)从2个pionts的坐标中找到线系数

2)找到piont的坐标,如何从它的系数

中交叉2行

3)从2点的坐标中找出段的长度

4)找到与此起点一致的piont的坐标,这个长度来自线系数和你在前一个函数中找到的起点和长度的坐标除以(4 * pi)

<强> EDIT3: 您可以优化此解决方案,它有一些缺陷,如何平行线等。但它快速,如果您优化它可能满足您的要求。

的Xaml:

<Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                        <PathFigure x:Name="pathleftdown" StartPoint="0,0">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment x:Name="bezleftdown" Point1="0,0"
                                           Point2="0,0"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
    <Path Stroke="Red" StrokeThickness="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure x:Name="pathrigthdown" StartPoint="0,0">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <QuadraticBezierSegment x:Name="bezrigthdown" Point1="0,0"
                                           Point2="0,0"
                                           />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>
    <Path Stroke="Red" StrokeThickness="1">
    <Path.Data>
        <PathGeometry>
            <PathGeometry.Figures>
                <PathFigureCollection>
                        <PathFigure x:Name="pathleftup"  StartPoint="0,0">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                    <QuadraticBezierSegment x:Name="bezleftup" Point1="0,0"
                                           Point2="0,0"
                                           />                                       
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathFigureCollection>
            </PathGeometry.Figures>
        </PathGeometry>
    </Path.Data>
</Path>
    <Path Stroke="Red" StrokeThickness="1">
        <Path.Data>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure x:Name="pathrigthup"  StartPoint="0,0">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <QuadraticBezierSegment x:Name="bezrigthup" Point1="0,0"
                                           Point2="0,0"
                                           />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </Path.Data>
    </Path>

    <Polygon Name="pol"  Points="0,0 250,0 251,340 0,341" Stroke="Red" StrokeThickness="1"></Polygon>
    <Button Content="Generate" Width ="80" Height="30" HorizontalAlignment="Right" VerticalAlignment="Top"  Click="Button_Click"></Button>

和代码:

private class pointXY
    {
        public double x;
        public double y;
    }
    private class lineKB
    {
        public double k;
        public double b;
        public bool flagXconst = false;
        public double xConst = 0;
    }

    private lineKB GetLineFromPonts(pointXY A, pointXY B)
    {
        lineKB line = new lineKB();
        if ((B.x - A.x) != 0)
        {
            line.k = (B.y - A.y) / (B.x - A.x);
            line.b = A.y - A.x * line.k;
        }
        else
        {
            line.xConst = A.x;
            line.flagXconst = true;
        }
        return line;
    }

    private pointXY GetPointFromLines(lineKB a, lineKB b)
    {
        pointXY point = new pointXY();
        if (a.flagXconst)
        {
            point.x = a.xConst;
            point.y = a.xConst * b.k + b.b;
        }else
            if (b.flagXconst)
            {
                point.x = b.xConst;
                point.y = b.xConst * a.k + a.b;
            }
            else
            {
                point.x = (a.b - b.b) / (b.k - a.k);
                point.y = a.k * point.x + a.b;
            }
        return point;
    }

    private double LengthOfLine(pointXY A, pointXY B)
    {
        return Math.Sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
    }

    private pointXY GetMidlePoint(pointXY S, double l, lineKB line, bool leftright)
    {
        double b = -2 * S.x - 2 * line.k * (-line.b + S.y);
        double a = (1 + line.k * line.k);
        double c = (S.x * S.x - l * l + (-line.b + S.y) * (-line.b + S.y));
        double d = b*b - 4 * a * c;
        double x1 = (-b + Math.Sqrt(d)) / (2 * a);
        double x2 = (-b - Math.Sqrt(d)) / (2 * a);
        pointXY ret = new pointXY();
        if (leftright)
            if (x1 > S.x) ret.x = x1;
            else ret.x = x2;
        else
            if (x1 < S.x) ret.x = x1;
            else ret.x = x2;
        ret.y = line.k * ret.x + line.b;
        return ret;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        pointXY A = new pointXY();
        A.x = pol.Points[0].X;
        A.y = pol.Points[0].Y;
        pointXY B = new pointXY();
        B.x = pol.Points[1].X;
        B.y = pol.Points[1].Y;
        pointXY C = new pointXY();
        C.x = pol.Points[2].X;
        C.y = pol.Points[2].Y;
        pointXY D = new pointXY();
        D.x = pol.Points[3].X;
        D.y = pol.Points[3].Y;
        lineKB AC = GetLineFromPonts(A, C);
        lineKB BD = GetLineFromPonts(B, D);
        pointXY R = GetPointFromLines(AC, BD);

        lineKB AB = GetLineFromPonts(A, B);
        lineKB BC = GetLineFromPonts(B, C);
        lineKB CD = GetLineFromPonts(C, D);
        lineKB DA = GetLineFromPonts(D, A);

        pointXY E = GetPointFromLines(AB, CD);
        lineKB ER = GetLineFromPonts(E, R);
        pointXY L = GetPointFromLines(ER, DA);
        pointXY N = GetPointFromLines(ER, BC);

        pointXY F = GetPointFromLines(BC, DA);
        lineKB FR = GetLineFromPonts(F, R);
        pointXY M = GetPointFromLines(FR, AB);
        pointXY O = GetPointFromLines(FR, CD);

        pointXY W = GetMidlePoint(A, (LengthOfLine(A, R) / (4 * Math.PI)), AC, true);
        pointXY X = GetMidlePoint(B, (LengthOfLine(B, R) / (4 * Math.PI)), BD, false);
        pointXY Y = GetMidlePoint(C, (LengthOfLine(C, R) / (4 * Math.PI)), AC, false);
        pointXY Z = GetMidlePoint(D, (LengthOfLine(D, R) / (4 * Math.PI)), BD, true);

        pathleftup.StartPoint = new Point(L.x, L.y);
        bezleftup.Point1 = new Point(W.x, W.y);
        bezleftup.Point2 = new Point(M.x, M.y);

        pathleftdown.StartPoint = new Point(L.x, L.y);
        bezleftdown.Point1 = new Point(Z.x, Z.y);
        bezleftdown.Point2 = new Point(O.x, O.y);

        pathrigthdown.StartPoint = new Point(O.x, O.y);
        bezrigthdown.Point1 = new Point(Y.x, Y.y);
        bezrigthdown.Point2 = new Point(N.x, N.y);

        pathrigthup.StartPoint = new Point(M.x, M.y);
        bezrigthup.Point1 = new Point(X.x, X.y);
        bezrigthup.Point2 = new Point(N.x, N.y);

    }

答案 1 :(得分:5)

根据Jeff M提供的信息,我创建了一个函数,该函数返回一个拟合在多边形中的椭圆:

Ellipse FitEllipse(Polygon poly)
    {
        double W0 = poly.Points[0].X;
        double W1 = poly.Points[0].Y;
        double X0 = poly.Points[1].X;
        double X1 = poly.Points[1].Y;
        double Y0 = poly.Points[2].X;
        double Y1 = poly.Points[2].Y;
        double Z0 = poly.Points[3].X;
        double Z1 = poly.Points[3].Y;

        double A =  X0 * Y0 * Z1 - W0 * Y0 * Z1 - X0 * Y1 * Z0 + W0 * Y1 * Z0 - W0 * X1 * Z0 + W1 * X0 * Z0 + W0 * X1 * Y0 - W1 * X0 * Y0;
        double B =  W0 * Y0 * Z1 - W0 * X0 * Z1 - X0 * Y1 * Z0 + X1 * Y0 * Z0 - W1 * Y0 * Z0 + W1 * X0 * Z0 + W0 * X0 * Y1 - W0 * X1 * Y0;
        double C =  X0 * Y0 * Z1 - W0 * X0 * Z1 - W0 * Y1 * Z0 - X1 * Y0 * Z0 + W1 * Y0 * Z0 + W0 * X1 * Z0 + W0 * X0 * Y1 - W1 * X0 * Y0;
        double D =  X1 * Y0 * Z1 - W1 * Y0 * Z1 - W0 * X1 * Z1 + W1 * X0 * Z1 - X1 * Y1 * Z0 + W1 * Y1 * Z0 + W0 * X1 * Y1 - W1 * X0 * Y1;
        double E = -X0 * Y1 * Z1 + W0 * Y1 * Z1 + X1 * Y0 * Z1 - W0 * X1 * Z1 - W1 * Y1 * Z0 + W1 * X1 * Z0 + W1 * X0 * Y1 - W1 * X1 * Y0;
        double F =  X0 * Y1 * Z1 - W0 * Y1 * Z1 + W1 * Y0 * Z1 - W1 * X0 * Z1 - X1 * Y1 * Z0 + W1 * X1 * Z0 + W0 * X1 * Y1 - W1 * X1 * Y0;
        double G =  X0 * Z1 - W0 * Z1 - X1 * Z0 + W1 * Z0 - X0 * Y1 + W0 * Y1 + X1 * Y0 - W1 * Y0;
        double H =  Y0 * Z1 - X0 * Z1 - Y1 * Z0 + X1 * Z0 + W0 * Y1 - W1 * Y0 - W0 * X1 + W1 * X0;
        double I =  Y0 * Z1 - W0 * Z1 - Y1 * Z0 + W1 * Z0 + X0 * Y1 - X1 * Y0 + W0 * X1 - W1 * X0;

        double detT = A * E * I + B * F * G + C * D * H - A * F * H - B * D * I - C * E * G;

        double J = (E * I - F * H) / detT;
        double K = (C * H - B * I) / detT;
        double L = (B * F - C * E) / detT;
        double M = (F * G - D * I) / detT;
        double N = (A * I - C * G) / detT;
        double O = (C * D - A * F) / detT;
        double P = (D * H - E * G) / detT;
        double Q = (B * G - A * H) / detT;
        double R = (A * E - B * D) / detT;

        double a = J * J + M * M + P * P;
        double b = J * K + M * N - P * Q;
        double c = K * K + N * N - Q * Q;
        double d = J * L + M * O - P * R;
        double f = K * L + N * O - Q * R;
        double g = L * L + O * O - R * R;

        double Ex = (c * d - b * f) / (b * b - a * c);
        double Ey = (a * f - b * d) / (b * b - a * c);

        double Ea = Math.Sqrt(2.0 * (a * f * f + c * d * d + g * b * b - 2.0 * b * d * f - a * c * g) / ((b * b - a * c) * (Math.Sqrt((a - c) * (a - c) + 4.0 * b * b) - (a + c))));
        double Eb = Math.Sqrt(2.0 * (a * f * f + c * d * d + g * b * b - 2.0 * b * d * f - a * c * g) / ((a * c - b * b) * (Math.Sqrt((a - c) * (a - c) + 4.0 * b * b) + (a + c))));

        double phi = 0;

        if (b == 0 && a < c) {
            phi = 0;
        } else if (b == 0 && a > c) {
            phi = Math.PI / 2;
        } else if (b != 0 && a < c) {
            phi = (Math.PI / 2 - Math.Atan((a - c) / (2 * b))) / 2;
        } else if (b != 0 && a > c) {
            phi = (Math.PI / 2 - Math.Atan((a - c) / (2 * b))) / 2 + Math.PI / 2;
        }

        Ellipse el = new Ellipse();
        el.Height = Ea * 2;
        el.Width  = Eb * 2;

        el.RenderTransform = new RotateTransform(phi * 180 / Math.PI);

        return el;
    }

答案 2 :(得分:2)

虽然四条bezier曲线应该做得很好(并且可能是最简单的解决方案),但我在这里提出了一种不同的方法,仅仅是为了踢。 : - )

以这种方式思考你的问题:给定一个规则的矩形,在里面画一个椭圆,然后将矩形变形到你的最终形状。

我不认为你的变形变换是线性的,所以你可能不能简单地为它找到一个矩阵(注意:我可能错了,并且希望被证明是错的;任何数学增益都在这里?)另见下面的编辑。

一种方法是:绘制椭圆,然后逐个拉/推四个角中的每一个。通过仅变形形状的一个角,您可以始终通过保持左右角之间的对角线完整地将椭圆曲线上的任何点插入到其新位置。

编辑:转换

从一个正方形(和一个圆圈)开始。

  1. 旋转45度,使对角线为轴
  2. 将两个轴缩放到正确的比例,使它们与新形状的两个对角线的相对长度相匹配
  3. 将一个轴倾斜到新形状的两个对角线之间的角度
  4. 翻译一个轴使原点与新形状的对角线的交叉点匹配
  5. 请注意,所有四个变换都是纯线性或仿射(即线性+平移),因此它们每个都可以用变换矩阵表示。最终结果是另一个仿射变换,也可以用矩阵表示。

    因此,您的圆形会被此矩阵转换为新形状。

    我希望自己在数学上没有犯错......

答案 3 :(得分:1)

Karsten的代码中有一个小错误

} else if (b != 0 && a < c) {
    phi = (Math.PI / 2 - Math.Atan((a - c) / (2 * b))) / 2;
} else if (b != 0 && a > c) {       

当a == c时返回phi = 0,这是不正确的。相反,上面的行应该是

} else if (b != 0 && a < c) {
    phi = (Math.PI / 2 - Math.Atan((a - c) / (2 * b))) / 2;
} else if (b != 0 && a >= c) {

} else if (b != 0 && a <= c) {
     phi = (Math.PI / 2 - Math.Atan((a - c) / (2 * b))) / 2;
} else if (b != 0 && a > c) {