سؤال

كنت أحاول القيام بذلك طوال اليوم. في الأساس ، لدي خط ونقطة. أريد أن ينحني الخط ويمر عبر هذه النقطة ، لكنني لا أريد منحنى سلس. لا يمكنني أن أكون قادرًا على تحديد عدد الخطوات الموجودة في المنحنى ، مثل ذلك (احذر رسم Mspaint الخام):curve

وهلم جرا. جربت أشياء مختلفة ، مثل أخذ الزاوية من مركز الخط الأولي ثم تقسيم الخط عند النقطة التي تؤدي فيها الزاوية ، لكن لدي مشكلة مع الطول. أود فقط أن آخذ الطول الأولي وأقسمه على عدد الخطوات التي كنت فيها ، لكن هذا لم يكن صحيحًا تمامًا.

أي شخص يعرف طريقة للقيام بذلك؟

شكرًا.

هل كانت مفيدة؟

المحلول

ربما تحتاج إلى ترميز هذا بنفسك. أعتقد أنه يمكنك القيام بذلك عن طريق تنفيذ وظيفة منحنى بيزير التربيعية في الكود ، والتي يمكن العثور عليها هنا. أنت تقرر مدى غرابة الزيادات عن طريق حل فقط لبعض القيم. إذا كنت تريد خطًا مستقيمًا ، فحلِل فقط لـ 0 و 1 وتوصيل تلك النقاط بالخطوط. إذا كنت تريد مثال زاوية واحدة ، فقم بحل 0 و 0.5 و 1 وتوصيل النقاط بالترتيب. إذا كنت تريد مثالك الثالث ، فقم بحل 0 و 0.25 و 0.5 و 0.75 و 1. من المحتمل أن يكون من الأفضل وضعه في حلقة مثل هذا:

float stepValue = (float)0.25;
float lastCalculatedValue;
for (float t = 0; t <= 1; t += stepValue)
{
    // Solve the quadratic bezier function to get the point at t.
    // If this is not the first point, connect it to the previous point with a line.
    // Store the new value in lastCalculatedValue.
}

تحرير: في الواقع ، يبدو أنك تريد أن يمر الخط عبر نقطة التحكم الخاصة بك. إذا كان هذا هو الحال ، فأنت لا ترغب في استخدام منحنى بيزيير تربيعي. بدلاً من ذلك ، ربما تريد منحنى Lagrange. قد يساعد هذا الموقع في المعادلة: http://www.math.ucla.edu/~baker/java/hoefer/lagrange.htm. ولكن في كلتا الحالتين ، يمكنك استخدام نفس النوع من الحلقة للتحكم في درجة نعومة.

التحرير الثاني: يبدو أن هذا يعمل. ما عليك سوى تغيير عضو NumberOfSteps ليكون العدد الإجمالي لقطاعات الخط التي تريدها وتعيين مجموعة النقاط بشكل مناسب. بالمناسبة ، يمكنك استخدام أكثر من ثلاث نقاط. سوف يوزع فقط إجمالي عدد شرائح الخط عبرها. لكنني قمت بتهيئة المصفوفة بحيث تبدو النتيجة مثالك الأخير.

التحرير الثالث: قمت بتحديث الرمز قليلاً حتى تتمكن من الضغط على النموذج لإضافة نقاط والنقر بزر الماوس الأيمن لإزالة النقطة الأخيرة. أيضًا ، أضفت Numericupdown إلى الأسفل حتى تتمكن من تغيير عدد القطاعات في وقت التشغيل.

public class Form1 : Form
{
    private int numberOfSegments = 4;

    private double[,] multipliers;
    private List<Point> points;

    private NumericUpDown numberOfSegmentsUpDown;

    public Form1()
    {
        this.numberOfSegmentsUpDown = new NumericUpDown();
        this.numberOfSegmentsUpDown.Value = this.numberOfSegments;
        this.numberOfSegmentsUpDown.ValueChanged += new System.EventHandler(this.numberOfSegmentsUpDown_ValueChanged);
        this.numberOfSegmentsUpDown.Dock = DockStyle.Bottom;
        this.Controls.Add(this.numberOfSegmentsUpDown);

        this.points = new List<Point> { 
            new Point(100, 110), 
            new Point(50, 60), 
            new Point(100, 10)};

        this.PrecomputeMultipliers();
    }

    public void PrecomputeMultipliers()
    {
        this.multipliers = new double[this.points.Count, this.numberOfSegments + 1];

        double pointCountMinusOne = (double)(this.points.Count - 1);

        for (int currentStep = 0; currentStep <= this.numberOfSegments; currentStep++)
        {
            double t = currentStep / (double)this.numberOfSegments;

            for (int pointIndex1 = 0; pointIndex1 < this.points.Count; pointIndex1++)
            {
                double point1Weight = pointIndex1 / pointCountMinusOne;

                double currentMultiplier = 1;
                for (int pointIndex2 = 0; pointIndex2 < this.points.Count; pointIndex2++)
                {
                    if (pointIndex2 == pointIndex1)
                        continue;

                    double point2Weight = pointIndex2 / pointCountMinusOne;
                    currentMultiplier *= (t - point2Weight) / (point1Weight - point2Weight);
                }

                this.multipliers[pointIndex1, currentStep] = currentMultiplier;
            }
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        Point? previousPoint = null;
        for (int currentStep = 0; currentStep <= numberOfSegments; currentStep++)
        {
            double sumX = 0;
            double sumY = 0;
            for (int pointIndex = 0; pointIndex < points.Count; pointIndex++)
            {
                sumX += points[pointIndex].X * multipliers[pointIndex, currentStep];
                sumY += points[pointIndex].Y * multipliers[pointIndex, currentStep];
            }

            Point newPoint = new Point((int)Math.Round(sumX), (int)Math.Round(sumY));

            if (previousPoint.HasValue)
                e.Graphics.DrawLine(Pens.Black, previousPoint.Value, newPoint);

            previousPoint = newPoint;
        }

        for (int pointIndex = 0; pointIndex < this.points.Count; pointIndex++)
        {
            Point point = this.points[pointIndex];
            e.Graphics.FillRectangle(Brushes.Black, new Rectangle(point.X - 1, point.Y - 1, 2, 2));
        }
    }

    protected override void OnMouseClick(MouseEventArgs e)
    {
        base.OnMouseClick(e);

        if (e.Button == MouseButtons.Left)
        {
            this.points.Add(e.Location);
        }
        else
        {
            this.points.RemoveAt(this.points.Count - 1);
        }

        this.PrecomputeMultipliers();
        this.Invalidate();
    }

    private void numberOfSegmentsUpDown_ValueChanged(object sender, EventArgs e)
    {
        this.numberOfSegments = (int)this.numberOfSegmentsUpDown.Value;
        this.PrecomputeMultipliers();
        this.Invalidate();
    }
}

نصائح أخرى

يمكنك الذهاب في العكس: ابحث أولاً عن منحنى مطابقة ثم استخدم النقاط الموجودة على المنحنى لرسم الخطوط. فمثلا:

alt text

تم الحصول على هذه المؤامرة بالطريقة التالية:

افترض أن لديك ثلاث نقاط انطلاق {x0،0} ، {x1 ، y1} ، {x2،0}

ثم تجد منحنيان مكافئين يتقاطعان في {x1 ، y1} ، مع شرط إضافي من وجود الحد الأقصى في تلك المرحلة (للانتقال السلس). تلك المنحنيات هي:

 yLeft[x_] := a x^2 + b x + c; 
 yRight[x_] := d x^2 + e x + f;

حيث نجد (بعد بعض التفاضل والتكامل):

   {c -> -((-x0^2 y1 + 2 x0 x1 y1)/(x0 - x1)^2), 
    a -> -(y1/(x0 - x1)^2), 
    b -> (2 x1 y1)/(-x0 + x1)^2}  

و

   {f -> -((2 x1 x2 y1 - x2^2 y1)/(x1 - x2)^2), 
    d -> -(y1/(x1 - x2)^2), 
    e -> (2 x1 y1)/(x1 - x2)^2}

لذلك لدينا منحنينا.

الآن يجب أن تلاحظ أنه إذا كنت تريد أن تكون نقاطك متباعدة بالتساوي ، فيجب أن تكون X1/X2 رقمًا عقلانيًا. وخياراتك للخطوات محدودة. قد تختار خطوات تمر على X1 و X2 أثناء بدء تشغيل X0. (تلك هي من النموذج X1/(N * x2))

و هذا كل شيء. أنت الآن تشكل خطوطك وفقًا للنقاط {x ، yleft [x]} أو {x ، yright [x]} اعتمادًا على أي جانب من x1 أنت.

ملاحظة: قد تختار رسم منحنى مكافئ واحد فقط يمر بنقاطك الثلاث ، ولكنه سيؤدي إلى عدم تناسق كبير في الحالة العامة.

إذا كانت النقطة X1 في الوسط ، فإن النتائج أجمل:

alt text

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top