4 очка и эллипс - PullRequest
       15

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

22 голосов
/ 27 марта 2011

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

9 голосов
/ 28 марта 2011

Одним из способов является использование 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.

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

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

С помощью этого алгоритма я нахожу точку для 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 пионтов

2) найти координаты Пионта как пересечь 2 линии из его коэффициентов

3) найти длину отрезка по координатам 2 точек

4) найти координаты пионта, который находится на линии с этой начальной точкой и этой длиной из линейных коэффициентов и координат начальной точки и длины, которые вы найдете в предыдущей функции, деленными на (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);

    }
5 голосов
/ 28 марта 2011

С помощью информации, предоставленной Джеффом, М.И. создал функцию, которая возвращает фитинг эллипса в многоугольнике:

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 голосов
/ 30 марта 2011

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

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

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

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

РЕДАКТИРОВАТЬ: ТРАНСФОРМАТИЧЕСКИЙ

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

  1. Поверните на 45 градусов так, чтобы диагонали были осями
  2. Масштабируйте обе оси до правильных шкал, чтобы они соответствовали относительной длинедвух диагоналей новой фигуры
  3. наклоните одну ось к углу между двумя диагоналями новой фигуры
  4. Переведите одну ось, чтобы точка начала соответствовала точке пересечения диагоналей новой фигуры

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

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

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

1 голос
/ 01 июля 2011

В коде Карстена есть небольшая ошибка около

} 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) {
...