Domanda

Ho cercato di fare questo tutto il giorno. Fondamentalmente, Ho una linea e un punto. Voglio la linea curva e passare attraverso quel punto, ma io non voglio una curva regolare. Ho wan't essere in grado di definire il numero di passaggi nella mia curva, in questo modo (attenzione disegno mspaint grezzo): curva

E così via. Ho provato varie cose, come prendere l'angolo dal centro della linea iniziale e poi suddividere la linea nel punto in cui i cavi di angolo, ma ho un problema con la lunghezza. Vorrei solo prendere la lunghezza iniziale e dividerlo per il numero di passi sono stato a, ma non era abbastanza di destra.

qualcuno conosce un modo per farlo?

Grazie.

È stato utile?

Soluzione

Si sarebbe probabilmente bisogno di codice da soli. Credo che si potrebbe fare mediante l'attuazione di una funzione curva di Bezier quadratica nel codice, che può essere trovato qui . Siete voi a decidere come si vuole bene gli incrementi da solo risolvendo per alcuni valori. Se si desidera una linea retta, solo per risolvere i 0 e 1 e collegare i punti con le linee. Se si desidera che l'esempio un angolo, risolvere per 0, 0.5, e 1 e collegare i punti in ordine. Se volete che il vostro terzo esempio, risolvere per 0, 0.25, 0.5, 0.75, e 1. Probabilmente sarebbe meglio metterlo in un ciclo come questo:

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

Edit: In realtà, sembra che si desidera che la linea passi attraverso il punto di controllo. Se questo è il caso, non si desidera utilizzare una curva di Bézier quadratica. Invece, probabilmente si desidera una curva Lagrange. Questo sito potrebbe aiutare con l'equazione: http: //www.math. ucla.edu/~baker/java/hoefer/Lagrange.htm . Ma in entrambi i casi, è possibile utilizzare lo stesso tipo di ciclo per controllare il grado di scorrevolezza.

2 ° Edit: Questo sembra funzionare. Basta cambiare il membro numberOfSteps di essere il numero complessivo di segmenti di linea che si desidera e impostare l'array punti in modo appropriato. Tra l'altro, è possibile utilizzare più di tre punti. Sarà solo distribuire il numero totale di segmenti di linea attraverso loro. Ma io inizializzato l'array in modo che gli sguardi di risultato come il vostro ultimo esempio.

3 ° Edit: ho aggiornato il codice un po 'in modo da poter sinistro fare clic sul form per aggiungere punti e fare clic destro per rimuovere l'ultimo punto. Inoltre, ho aggiunto un NumericUpDown verso il basso in modo da poter cambiare il numero di segmenti in fase di esecuzione.

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

Altri suggerimenti

Si potrebbe andare il contrario: in primo luogo trovare una curva corrispondente e quindi utilizzare i punti sulla curva per disegnare le linee. Ad esempio:

alt text

Questo terreno è stata ottenuta nel seguente modo:

Si supponga di avere i tre punti di partenza {x0,0}, {x1, y1}, {x2,0}

Poi si trovano due curve paraboliche si intersecano a {x1, y1}, con l'ulteriore condizione di avere un massimi a quel punto (per una transizione senza problemi). Quelle curve sono:

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

Dove troviamo (dopo un po 'di calcolo):

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

e

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

così abbiamo i nostri due curve.

Ora si dovrebbe notare che se si desidera che i punti equidistanti, x1 / x2 dovrebbe essere un razionale number.and le scelte per le fasi sono limitati. Si può scegliere fasi di passaggio X1 e X2, mentre a partire dal x0. (Quelli sono della forma x1 / (n * x2))

E questo è tutto. Ora si formano le linee secondo i punti {x, yLeft [x]} o {x, yright [x]} a seconda di quale lato del x1 sei.

Nota: Si può scegliere di disegnare una sola curva parabolica che passano dai vostri tre punti, ma si tradurrà altamente asimmetrica nel caso generale.

Se il punto di x1 è nel mezzo, i risultati sono più bello:

alt text

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top