So verwenden Sie Javascript zum Generieren glatter Kurven

So verwenden Sie Javascript zum Generieren glatter Kurven

Vorwort

bild.png

Die Erzeugung glatter Kurven ist eine sehr praktische Technologie

Oft müssen wir einige Polylinien zeichnen und sie dann vom Computer reibungslos verbinden lassen.

Schauen wir uns zunächst den endgültigen Effekt an (die rote Linie ist die gerade Linie, die wir eingegeben haben, und die blaue Linie ist die Kurve nach der Anpassung). Der Anfang und das Ende können speziell bearbeitet werden, damit das Diagramm besser aussieht:)

Jul-09-2021 15-28-04.gif

Die Idee ist, die Bezier-Kurve zur Anpassung zu verwenden

Einführung in Bézierkurven

Die Bézierkurve ist eine sehr wichtige parametrische Kurve in der Computergrafik.

Quadratische Bézier-Kurve

240px-Bézier_2_big.gif

Der Verlauf einer quadratischen Bézierkurve wird durch die Funktion B(t) bei gegebenen Punkten P0, P1 und P2 beschrieben:

bild.png

Kubische Bézierkurve

240px-Bézier_3_big.gif

Bei einer kubischen Kurve kann sie durch die Zwischenpunkte Q0, Q1, Q2, die durch die lineare Bézierkurve beschrieben werden, und die Punkte R0 und R1, die durch die quadratische Kurve beschrieben werden, konstruiert werden.

bild.png

Bézierkurven-Berechnungsfunktion

Nach der obigen Formel erhalten wir die Berechnungsfunktion

Zweite Ordnung

  /**
   *
   *
   * @param {Zahl} p0
   * @param {Zahl} p1
   * @param {Zahl} p2
   * @param {Zahl} t
   * @zurückkehren {*}
   * @memberof Pfad
   */
  bezier2P(p0: Zahl, p1: Zahl, p2: Zahl, t: Zahl) {
    const P0 = p0 * Math.pow(1 - t, 2);
    Konstante P1 = p1 * 2 * t * (1 - t);
    Konstante P2 = p2 * t * t;
    gib P0 + P1 + P2 zurück;
  }
  
    /**
   *
   *
   * @param {Punkt} p0
   * @param {Punkt} p1
   * @param {Punkt} p2
   * @param {number} Zahl
   * @param {Zahl} Häkchen
   * @return {*} {Punkt}
   * @memberof Pfad
   */
  getBezierNowPoint2P(
      p0: Punkt,
      p1: Punkt,
      p2: Punkt,
      num: Zahl,
      Häkchen: Zahl,
  ): Punkt {
    zurückkehren {
      x: this.bezier2P(p0.x, p1.x, p2.x, num * tick),
      y: this.bezier2P(p0.y, p1.y, p2.y, Num * Tick),
    };
  }
  
    /**
   * Generieren Sie quadratische Bézierkurven-Scheitelpunktdaten*
   * @param {Punkt} p0
   * @param {Punkt} p1
   * @param {Punkt} p2
   * @param {Zahl} [Zahl=100]
   * @param {Zahl} [Häkchen=1]
   * @zurückkehren {*}
   * @memberof Pfad
   */
  erstelle2PBezier(
      p0: Punkt,
      p1: Punkt,
      p2: Punkt,
      num: Zahl = 100,
      Häkchen: Zahl = 1,
  ) {
    const t = Tick / (Zahl - 1);
    const Punkte = [];
    für (sei i = 0; i < num; i++) {
      const Punkt = this.getBezierNowPoint2P(p0, p1, p2, i, t);
      Punkte.push({x: Punkt.x, y: Punkt.y});
    }
    Rückgabepunkte;
  }

Dritte Ebene

/**
   * Formel für die kubische Searl-Kurve*
   * @param {Zahl} p0
   * @param {Zahl} p1
   * @param {Zahl} p2
   * @param {Zahl} p3
   * @param {Zahl} t
   * @zurückkehren {*}
   * @memberof Pfad
   */
  bezier3P(p0: Zahl, p1: Zahl, p2: Zahl, p3: Zahl, t: Zahl) {
    const P0 = p0 * Math.pow(1 - t, 3);
    const P1 = 3 * p1 * t * Math.pow(1 - t, 2);
    const P2 = 3 * p2 * Math.pow(t, 2) * (1 - t);
    const P3 = p3 * Math.pow(t, 3);
    gebe P0 + P1 + P2 + P3 zurück;
  }
  
    /**
   * Koordinaten abrufen *
   * @param {Punkt} p0
   * @param {Punkt} p1
   * @param {Punkt} p2
   * @param {Punkt} p3
   * @param {number} Zahl
   * @param {Zahl} Häkchen
   * @zurückkehren {*}
   * @memberof Pfad
   */
  getBezierNowPoint3P(
      p0: Punkt,
      p1: Punkt,
      p2: Punkt,
      p3: Punkt,
      num: Zahl,
      Häkchen: Zahl,
  ) {
    zurückkehren {
      x: this.bezier3P(p0.x, p1.x, p2.x, p3.x, num * tick),
      y: this.bezier3P(p0.y, p1.y, p2.y, p3.y, Num * Tick),
    };
  }
  
    /**
   * Kubische Bézierkurven-Scheitelpunktdaten generieren*
   * @param {Point} p0 Startpunkt {x: Zahl, y: Zahl}
   * @param {Punkt} p1 Kontrollpunkt 1 { x : Zahl, y : Zahl}
   * @param {Punkt} p2 Kontrollpunkt 2 { x : Zahl, y : Zahl}
   * @param {Punkt} p3 Endpunkt {x: Zahl, y: Zahl}
   * @param {Zahl} [Zahl=100]
   * @param {Zahl} [Häkchen=1]
   * @return {Punkt[]}
   * @memberof Pfad
   */
  erstelle3PBezier(
      p0: Punkt,
      p1: Punkt,
      p2: Punkt,
      p3: Punkt,
      num: Zahl = 100,
      Häkchen: Zahl = 1,
  ) {
    const PunktMum = Num;
    const _tick = ticken;
    const t = _tick / (pointMum - 1);
    const Punkte = [];
    für (sei i = 0; i < pointMum; i++) {
      const Punkt = this.getBezierNowPoint3P(p0, p1, p2, p3, i, t);
      Punkte.push({x: Punkt.x, y: Punkt.y});
    }
    Rückgabepunkte;
  }

Anpassungsalgorithmus

bild.png

Das Problem ist, wie man die Kontrollpunkte erhält. Wir verwenden eine relativ einfache Methode

Nehmen Sie die Winkelhalbierende c1c2 von p1-pt-p2, die senkrecht zur Winkelhalbierenden c2 steht. Nehmen Sie die kurze Seite als Länge von c1-pt c2-pt. Skalieren Sie die Länge. Diese Länge kann grob als Krümmung der Kurve verstanden werden.

bild.png

Das ab-Liniensegment wird hier einfach verarbeitet und verwendet nur die Kurvengenerierung zweiter Ordnung -> 🌈 Sie können es nach Ihren persönlichen Vorstellungen verarbeiten

Das bc-Liniensegment verwendet den von abc berechneten Kontrollpunkt c2 und den von bcd berechneten Kontrollpunkt c3 und so weiter.

  /**
   * Kontrollpunkte, die zur Erzeugung einer glatten Kurve erforderlich sind *
   * @param {Vector2D} p1
   * @param {Vector2D} pt
   * @param {Vector2D} p2
   * @param {Zahl} [Verhältnis=0,3]
   * @zurückkehren {*}
   * @memberof Pfad
   */
  erstelleSmoothLineControlPoint(
      p1: Vektor2D,
      pt: Vektor2D,
      p2: Vektor2D,
      Verhältnis: Zahl = 0,3,
  ) {
    const vec1T: Vektor2D = Vektor2dMinus(p1, pt);
    const vecT2: Vektor2D = Vektor2dMinus(p1, pt);
    const len1: Zahl = vec1T.Länge;
    const len2: Zahl = vecT2.Länge;
    const v: Zahl = Länge1 / Länge2;
    sei Delta;
    wenn (v > 1) {
      delta = Vektor2dMinus(
          Seite 1,
          Vektor2dPlus(pt, Vektor2dMinus(p2, pt).Skala(1 / v)),
      );
    } anders {
      delta = Vektor2dMinus(
          Vektor2dPlus(pt, Vektor2dMinus(p1, pt).Skala(v)),
          Seite 2,
      );
    }
    delta = delta.scale(Verhältnis);
    const control1: Punkt = {
      x: Vektor2dPlus(pt, delta).x,
      y: Vektor2dPlus(pt, delta).y,
    };
    const control2: Punkt = {
      x: Vektor2dMinus(pt, delta).x,
      y: Vektor2dMinus(pt, delta).y,
    };
    gebe {Steuerung1, Steuerung2} zurück;
  }
  
    /**
   * Sanfte Kurvengenerierung *
   * @param {Point[]} Punkte
   * @param {number} Verhältnis
   * @zurückkehren {*}
   * @memberof Pfad
   */
  createSmoothLine(Punkte: Punkt[], Verhältnis: Zahl = 0,3) {
    const len ​​= Punkte.Länge;
    let resultPoints = [];
    const Kontrollpunkte = [];
    wenn (Länge < 3) return;
    für (sei i = 0; i < len - 2; i++) {
      const {Kontrolle1, Kontrolle2} = this.createSmoothLineControlPoint(
          neuer Vector2D(Punkte[i].x, Punkte[i].y),
          neuer Vector2D(Punkte[i + 1].x, Punkte[i + 1].y),
          neuer Vektor2D(Punkte[i + 2].x, Punkte[i + 2].y),
          Verhältnis,
      );
      Kontrollpunkte.push(Kontrolle1);
      Kontrollpunkte.push(Kontrolle2);
      lass Punkte1;
      lass Punkte2;

      // Der erste Kontrollpunkt verwendet nur einen if (i === 0) {
        Punkte1 = this.create2PBezier(Punkte[i], Kontrolle1, Punkte[i + 1], 50);
      } anders {
        console.log(Kontrollpunkte);
        Punkte1 = this.create3PBezier(
            Punkte[i],
            Kontrollpunkte[2 * i - 1],
            Steuerung1,
            Punkte[i + 1],
            50,
        );
      }
      // Endteil if (i + 2 === len - 1) {
        Punkte2 = this.create2PBezier(
            Punkte[i + 1],
            Steuerung2,
            Punkte[i + 2],
            50,
        );
      }

      wenn (i + 2 === Länge - 1) {
        ErgebnisPunkte = [...ErgebnisPunkte, ...Punkte1, ...Punkte2];
      } anders {
        ErgebnisPunkte = [...ErgebnisPunkte, ...Punkte1];
      }
    }
    gib Ergebnispunkte zurück;
  }

Beispielcode

    const Eingabe = [
        { x: 0, y: 0 },
        { x: 150, y: 150 },
        { x: 300, y: 0 },
        { x: 400, y: 150 },
        { x: 500, y: 0 },
        { x: 650, y: 150 },
    ]
    const s = Pfad.createSmoothLine(Eingabe);
    Lassen Sie ctx = document.getElementById('cv').getContext('2d');
    ctx.strokeStyle = "blau";
    ctx.beginPath();
    ctx.moveTo(0, 0);
    für (sei i = 0; i < s.Länge; i++) {
        ctx.lineTo(s[i].x, s[i].y);
    }
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(0, 0);
    für (lass i = 0; i < Eingabelänge; i++) {
        ctx.lineTo(Eingabe[i].x, Eingabe[i].y);
    }
    ctx.strokeStyle = "rot";
    ctx.stroke();
    document.getElementById('btn').addEventListener('klicken', () => {
        let app = document.getElementById('app');
        lass Index = 0;
        lass bewegen = () => {
            wenn (Index < s.Länge) {
                app.style.left = s[index].x - 10 + 'px';
                app.style.top = s[index].y - 10 + 'px';
                Index++;
                requestAnimationFrame(verschieben)
            }
        }
        bewegen()
    })

Anhang: Vector2D-bezogener Code

/**
 *
 *
 * @Klasse Vector2D
 * @extends {Array}
 */
Klasse Vector2D erweitert Array {
  /**
   * Erstellt eine Instanz von Vector2D.
   * @param {Zahl} [x=1]
   * @param {Zahl} [y=0]
   * @Mitglied von Vector2D
   * */
  Konstruktor(x: Zahl = 1, y: Zahl = 0) {
    super();
    dies.x = x;
    dies.y = y;
  }

  /**
   *
   * @param {Zahl} v
   * @Mitglied von Vector2D
   */
  Menge x(v) {
    dies[0] = v;
  }

  /**
   *
   * @param {Zahl} v
   * @Mitglied von Vector2D
   */
  setze y(v) {
    dies[1] = v;
  }

  /**
   *
   *
   * @readonly
   * @Mitglied von Vector2D
   */
  erhalte x() {
    gib dies zurück[0];
  }

  /**
   *
   *
   * @readonly
   * @Mitglied von Vector2D
   */
  bekomme y() {
    gib dies zurück[1];
  }

  /**
   *
   *
   * @readonly
   * @Mitglied von Vector2D
   */
  Länge abrufen() {
    gibt Math.hypot(dieses.x, dies.y) zurück;
  }

  /**
   *
   *
   * @readonly
   * @Mitglied von Vector2D
   */
  hol dir() {
    gibt Math.atan2(dieses.y, dieses.x) zurück;
  }

  /**
   *
   *
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  kopieren() {
    gib einen neuen Vector2D (dieses.x, dieses.y) zurück;
  }

  /**
   *
   *
   * @param {*} v
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  hinzufügen(v) {
    dies.x += vx;
    dies.y += vy;
    gib dies zurück;
  }

  /**
   *
   *
   * @param {*} v
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  unter(v) {
    dies.x -= vx;
    dies.y -= vy;
    gib dies zurück;
  }

  /**
   *
   *
   * @param {*} ein
   * @return {Vector2D}
   * @Mitglied von Vector2D
   */
  Skala(a) {
    dies.x *= a;
    dies.y *= a;
    gib dies zurück;
  }

  /**
   *
   *
   * @param {*} rad
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  drehen(rad) {
    const c = Math.cos(rad);
    const s = Math.sin(rad);
    const [x, y] = dies;

    dies.x = x * c + y * -s;
    dies.y = x * s + y * c;

    gib dies zurück;
  }

  /**
   *
   *
   * @param {*} v
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  Kreuz(v) {
    gib dies.x * vy - vx * dies.y zurück;
  }

  /**
   *
   *
   * @param {*} v
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  Punkt(v) {
    gib dies.x * vx + vy * dies.y zurück;
  }

  /**
   * Normalisierung*
   * @zurückkehren {*}
   * @Mitglied von Vector2D
   */
  normalisieren() {
    gib diesen Maßstab zurück (1 / diese Länge);
  }
}

/**
 * Vektoraddition *
 * @param {*} vec1
 * @param {*} vec2
 * @return {Vector2D}
 */
Funktion Vektor2dPlus(vec1, vec2) {
  gibt einen neuen Vector2D zurück (vec1.x + vec2.x, vec1.y + vec2.y);
}

/**
 * Vektorsubtraktion *
 * @param {*} vec1
 * @param {*} vec2
 * @return {Vector2D}
 */
Funktion Vektor2dMinus(vec1, vec2) {
  gibt einen neuen Vector2D zurück (vec1.x – vec2.x, vec1.y – vec2.y);
}

exportiere {Vector2D, vector2dPlus, vector2dMinus};

Zusammenfassen

Dies ist das Ende dieses Artikels zur Verwendung von Javascript zum Generieren glatter Kurven. Weitere Informationen zum Generieren glatter Kurven mit Javascript finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

<<:  Leitfaden zur effizienten Nutzung von MySQL-Indizes

>>:  Implementierungscode für die Dateimontage von DockerToolBox

Artikel empfehlen

So installieren Sie Element UI und verwenden Vektorgrafiken in vue3.0

Hier konzentrieren wir uns nur auf die Installati...

Probleme bei der Ausführungsreihenfolge von AND und OR in SQL-Anweisungen

Frage Beim Schreiben von Datenbank-SQL ist mir ge...

Vue implementiert einen beweglichen schwebenden Button

In diesem Artikelbeispiel wird der spezifische Co...

Beispielanalyse der Auswirkungen des MySQL-Index auf die Sortierung

Dieser Artikel veranschaulicht anhand von Beispie...

Lösen Sie das Problem der blockierenden Positionierungs-DDL in MySQL 5.7

Im vorherigen Artikel „Änderungen der MySQL-Tabel...

Ein einfaches Beispiel für die Verwendung von Vue3-Routing VueRouter4

Routenplanung vue-router4 behält den Großteil der...

XHTML-Tags haben ein schließendes Tag

<br />Ursprünglicher Link: http://www.dudo.o...

So erstellen Sie schnell eine LNMP-Umgebung mit Docker (neueste Version)

Vorwort Tipp: Hier können Sie den ungefähren Inha...

Optimierung der Datenbank-SQL-Anweisung

Warum optimieren: Beim Start des eigentlichen Pro...

javascript:void(0) Bedeutung und Anwendungsbeispiele

Einführung in das Schlüsselwort void Zunächst ein...

So kaufen Sie einen Server und richten ihn zunächst ein

Ich habe eine Weile nicht mit Servern gearbeitet....