Подтвердить что ты не робот

4 очка и Эллипс

У меня есть 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

Вопрос по-прежнему unanswared!!!

4b9b3361

Ответ 1

Один из способов - использовать QuadraticBezierSegment или BezierSegment.

Например, например:

    <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

Edit: Пример для 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

Кривые

имеют начальную точку, среднюю точку и конечную точку. На этом изображении начало и конец piont L,M,N,O; и midle W,X,Y,Z.

Как, например, мы вычисляем точку L:

С помощью уравнения линии y = k * x + b находим уравнение линии AB,DC,AC,DB,AD. Как крест AC и DB находим R. Как крест AB и DC находим E. После этого находим уравнение линии ER и как крест ER и AD находим L.

Как мы вычисляем точку W:

С помощью уравнения для длины l = sqrt(sqr(x2 - x1) + sqr(y2 - y1)) найдите длину AR. AW = AR/(4*pi) и с помощью этого коэффициента и уравнения линии и уравнения для длины после решения квадратного уравнения находим W.

Другие точки мы находим аналогично.

Этот алгоритм не работает только для многоугольника, который имеет параллельную линию, но в этом случае алгоритм проще. И коэффициент для длины один и тот же.

С помощью этого алгоритма найдите точку для трех кривых вашего примера:

<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 пионтов

2) найти координаты piont, так как пересечение двух линий из него коэффициенты

3) найти длину отрезка из координат двух точек

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

    }

Ответ 2

С информацией, предоставленной 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;
    }

Ответ 3

Хотя четыре кривые безье должны делать это довольно хорошо (и, вероятно, это самое простое решение), здесь я предлагаю другой метод, просто для ударов.: -)

Подумайте о своей проблеме таким образом: задайте правильный прямоугольник, нарисуйте эллипс внутри, затем деформируйте прямоугольник в свою окончательную форму.

Я не думаю, что ваше преобразование деформации является линейным, поэтому вы, вероятно, не можете просто найти матрицу для него (обратите внимание: я могу ошибаться и хотел бы, чтобы вас доказали неправильно, любые математические баффы здесь?) Также см. ниже.

Один из способов: нарисуйте эллипс, затем потяните/нажимайте каждый из четырех углов один за другим. Деформируя только один угол формы, вы всегда можете интерполировать любую точку на кривой эллипса в новое положение, сохранив диагональ между левым и правым углами.

EDIT: TRANSFORM

Начните с квадрата (и внутри круга).

  • Повернуть на 45 градусов, чтобы диагонали были осями
  • Масштабируйте обе оси вправо, чтобы они соответствовали относительным длинам новой формы. Две диагонали
  • Наклоните одну ось на угол между двумя диагоналями новой формы
  • Переведите одну ось, чтобы точка начала совпала с точкой пересечения диагоналей новой формы.

Обратите внимание, что все четыре преобразования либо являются чисто линейными, либо аффинными (т.е. линейными + сдвигами), поэтому они кажутся представимыми с матрицей преобразования. Конечным результатом является другое аффинное преобразование, которое также может быть представлено матрицей.

Итак, ваша форма круга преобразуется этой матрицей в новую форму.

Надеюсь, я не ошибся в своей математике...

Ответ 4

Там небольшая ошибка в коде Карстен вокруг

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

Он возвращает phi = 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) {

или

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