91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么利用Javascript生成平滑曲線

發布時間:2022-04-25 16:03:27 來源:億速云 閱讀:154 作者:iii 欄目:大數據

這篇文章主要講解了“怎么利用Javascript生成平滑曲線”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么利用Javascript生成平滑曲線”吧!

前言

怎么利用Javascript生成平滑曲線

平滑曲線生成是一個很實用的技術

很多時候,我們都需要通過繪制一些折線,然后讓計算機平滑的連接起來,

先來看下最終效果(紅色為我們輸入的直線,藍色為擬合過后的曲線) 首尾可以特殊處理讓圖形看起來更好:)

怎么利用Javascript生成平滑曲線

實現思路是利用貝塞爾曲線進行擬合

貝塞爾曲線簡介

貝塞爾曲線(英語:Bézier curve)是計算機圖形學中相當重要的參數曲線。

二次貝塞爾曲線

怎么利用Javascript生成平滑曲線

二次方貝塞爾曲線的路徑由給定點P0、P1、P2的函數B(t)追蹤:

怎么利用Javascript生成平滑曲線

三次貝塞爾曲線

怎么利用Javascript生成平滑曲線

對于三次曲線,可由線性貝塞爾曲線描述的中介點Q0、Q1、Q2,和由二次曲線描述的點R0、R1所建構

怎么利用Javascript生成平滑曲線

貝塞爾曲線計算函數

根據上面的公式我們可有得到計算函數

二階

  /**
   *
   *
   * @param {number} p0
   * @param {number} p1
   * @param {number} p2
   * @param {number} t
   * @return {*}
   * @memberof Path
   */
  bezier2P(p0: number, p1: number, p2: number, t: number) {
    const P0 = p0 * Math.pow(1 - t, 2);
    const P1 = p1 * 2 * t * (1 - t);
    const P2 = p2 * t * t;
    return P0 + P1 + P2;
  }
  
    /**
   *
   *
   * @param {Point} p0
   * @param {Point} p1
   * @param {Point} p2
   * @param {number} num
   * @param {number} tick
   * @return {*}  {Point}
   * @memberof Path
   */
  getBezierNowPoint2P(
      p0: Point,
      p1: Point,
      p2: Point,
      num: number,
      tick: number,
  ): Point {
    return {
      x: this.bezier2P(p0.x, p1.x, p2.x, num * tick),
      y: this.bezier2P(p0.y, p1.y, p2.y, num * tick),
    };
  }
  
    /**
   * 生成二次方貝塞爾曲線頂點數據
   *
   * @param {Point} p0
   * @param {Point} p1
   * @param {Point} p2
   * @param {number} [num=100]
   * @param {number} [tick=1]
   * @return {*}
   * @memberof Path
   */
  create2PBezier(
      p0: Point,
      p1: Point,
      p2: Point,
      num: number = 100,
      tick: number = 1,
  ) {
    const t = tick / (num - 1);
    const points = [];
    for (let i = 0; i < num; i++) {
      const point = this.getBezierNowPoint2P(p0, p1, p2, i, t);
      points.push({x: point.x, y: point.y});
    }
    return points;
  }

三階

/**
   * 三次方塞爾曲線公式
   *
   * @param {number} p0
   * @param {number} p1
   * @param {number} p2
   * @param {number} p3
   * @param {number} t
   * @return {*}
   * @memberof Path
   */
  bezier3P(p0: number, p1: number, p2: number, p3: number, t: number) {
    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);
    return P0 + P1 + P2 + P3;
  }
  
    /**
   * 獲取坐標
   *
   * @param {Point} p0
   * @param {Point} p1
   * @param {Point} p2
   * @param {Point} p3
   * @param {number} num
   * @param {number} tick
   * @return {*}
   * @memberof Path
   */
  getBezierNowPoint3P(
      p0: Point,
      p1: Point,
      p2: Point,
      p3: Point,
      num: number,
      tick: number,
  ) {
    return {
      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),
    };
  }
  
    /**
   * 生成三次方貝塞爾曲線頂點數據
   *
   * @param {Point} p0 起始點  { x : number, y : number}
   * @param {Point} p1 控制點1 { x : number, y : number}
   * @param {Point} p2 控制點2 { x : number, y : number}
   * @param {Point} p3 終止點  { x : number, y : number}
   * @param {number} [num=100]
   * @param {number} [tick=1]
   * @return {Point []}
   * @memberof Path
   */
  create3PBezier(
      p0: Point,
      p1: Point,
      p2: Point,
      p3: Point,
      num: number = 100,
      tick: number = 1,
  ) {
    const pointMum = num;
    const _tick = tick;
    const t = _tick / (pointMum - 1);
    const points = [];
    for (let i = 0; i < pointMum; i++) {
      const point = this.getBezierNowPoint3P(p0, p1, p2, p3, i, t);
      points.push({x: point.x, y: point.y});
    }
    return points;
  }

擬合算法

怎么利用Javascript生成平滑曲線

問題在于如何得到控制點,我們以比較簡單的方法

取 p1-pt-p2的角平分線 c1c2垂直于該條角平分線 c2為p2的投影點取短邊作為c1-pt c2-pt的長度對該長度進行縮放 這個長度可以大概理解為曲線的彎曲程度

怎么利用Javascript生成平滑曲線

ab線段 這里簡單處理 只使用了二階的曲線生成 -> ? 這里可以按照個人想法處理

bc線段使用abc計算出來的控制點c2和bcd計算出來的控制點c3 以此類推

  /**
   * 生成平滑曲線所需的控制點
   *
   * @param {Vector2D} p1
   * @param {Vector2D} pt
   * @param {Vector2D} p2
   * @param {number} [ratio=0.3]
   * @return {*}
   * @memberof Path
   */
  createSmoothLineControlPoint(
      p1: Vector2D,
      pt: Vector2D,
      p2: Vector2D,
      ratio: number = 0.3,
  ) {
    const vec1T: Vector2D = vector2dMinus(p1, pt);
    const vecT2: Vector2D = vector2dMinus(p1, pt);
    const len1: number = vec1T.length;
    const len2: number = vecT2.length;
    const v: number = len1 / len2;
    let delta;
    if (v > 1) {
      delta = vector2dMinus(
          p1,
          vector2dPlus(pt, vector2dMinus(p2, pt).scale(1 / v)),
      );
    } else {
      delta = vector2dMinus(
          vector2dPlus(pt, vector2dMinus(p1, pt).scale(v)),
          p2,
      );
    }
    delta = delta.scale(ratio);
    const control1: Point = {
      x: vector2dPlus(pt, delta).x,
      y: vector2dPlus(pt, delta).y,
    };
    const control2: Point = {
      x: vector2dMinus(pt, delta).x,
      y: vector2dMinus(pt, delta).y,
    };
    return {control1, control2};
  }
  
    /**
   * 平滑曲線生成
   *
   * @param {Point []} points
   * @param {number} ratio
   * @return {*}
   * @memberof Path
   */
  createSmoothLine(points: Point[], ratio: number = 0.3) {
    const len = points.length;
    let resultPoints = [];
    const controlPoints = [];
    if (len < 3) return;
    for (let i = 0; i < len - 2; i++) {
      const {control1, control2} = this.createSmoothLineControlPoint(
          new Vector2D(points[i].x, points[i].y),
          new Vector2D(points[i + 1].x, points[i + 1].y),
          new Vector2D(points[i + 2].x, points[i + 2].y),
          ratio,
      );
      controlPoints.push(control1);
      controlPoints.push(control2);
      let points1;
      let points2;

      // 首端控制點只用一個
      if (i === 0) {
        points1 = this.create2PBezier(points[i], control1, points[i + 1], 50);
      } else {
        console.log(controlPoints);
        points1 = this.create3PBezier(
            points[i],
            controlPoints[2 * i - 1],
            control1,
            points[i + 1],
            50,
        );
      }
      // 尾端部分
      if (i + 2 === len - 1) {
        points2 = this.create2PBezier(
            points[i + 1],
            control2,
            points[i + 2],
            50,
        );
      }

      if (i + 2 === len - 1) {
        resultPoints = [...resultPoints, ...points1, ...points2];
      } else {
        resultPoints = [...resultPoints, ...points1];
      }
    }
    return resultPoints;
  }

案例代碼

const input = [
        { 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 = path.createSmoothLine(input);
    let ctx = document.getElementById("cv").getContext("2d");
    ctx.strokeStyle = "blue";
    ctx.beginPath();
    ctx.moveTo(0, 0);
    for (let i = 0; i < s.length; i++) {
        ctx.lineTo(s[i].x, s[i].y);
    }
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(0, 0);
    for (let i = 0; i < input.length; i++) {
        ctx.lineTo(input[i].x, input[i].y);
    }
    ctx.strokeStyle = "red";
    ctx.stroke();
    document.getElementById("btn").addEventListener("click", () => {
        let app = document.getElementById("app");
        let index = 0;
        let move = () => {
            if (index < s.length) {
                app.style.left = s[index].x - 10 + "px";
                app.style.top = s[index].y - 10 + "px";
                index++;
                requestAnimationFrame(move)
            }
        }
        move()
    })

附錄:Vector2D相關的代碼

/**
 *
 *
 * @class Vector2D
 * @extends {Array}
 */
class Vector2D extends Array {
  /**
   * Creates an instance of Vector2D.
   * @param {number} [x=1]
   * @param {number} [y=0]
   * @memberof Vector2D
   * */
  constructor(x: number = 1, y: number = 0) {
    super();
    this.x = x;
    this.y = y;
  }

  /**
   *
   * @param {number} v
   * @memberof Vector2D
   */
  set x(v) {
    this[0] = v;
  }

  /**
   *
   * @param {number} v
   * @memberof Vector2D
   */
  set y(v) {
    this[1] = v;
  }

  /**
   *
   *
   * @readonly
   * @memberof Vector2D
   */
  get x() {
    return this[0];
  }

  /**
   *
   *
   * @readonly
   * @memberof Vector2D
   */
  get y() {
    return this[1];
  }

  /**
   *
   *
   * @readonly
   * @memberof Vector2D
   */
  get length() {
    return Math.hypot(this.x, this.y);
  }

  /**
   *
   *
   * @readonly
   * @memberof Vector2D
   */
  get dir() {
    return Math.atan2(this.y, this.x);
  }

  /**
   *
   *
   * @return {*}
   * @memberof Vector2D
   */
  copy() {
    return new Vector2D(this.x, this.y);
  }

  /**
   *
   *
   * @param {*} v
   * @return {*}
   * @memberof Vector2D
   */
  add(v) {
    this.x += v.x;
    this.y += v.y;
    return this;
  }

  /**
   *
   *
   * @param {*} v
   * @return {*}
   * @memberof Vector2D
   */
  sub(v) {
    this.x -= v.x;
    this.y -= v.y;
    return this;
  }

  /**
   *
   *
   * @param {*} a
   * @return {Vector2D}
   * @memberof Vector2D
   */
  scale(a) {
    this.x *= a;
    this.y *= a;
    return this;
  }

  /**
   *
   *
   * @param {*} rad
   * @return {*}
   * @memberof Vector2D
   */
  rotate(rad) {
    const c = Math.cos(rad);
    const s = Math.sin(rad);
    const [x, y] = this;

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

    return this;
  }

  /**
   *
   *
   * @param {*} v
   * @return {*}
   * @memberof Vector2D
   */
  cross(v) {
    return this.x * v.y - v.x * this.y;
  }

  /**
   *
   *
   * @param {*} v
   * @return {*}
   * @memberof Vector2D
   */
  dot(v) {
    return this.x * v.x + v.y * this.y;
  }

  /**
   * 歸一
   *
   * @return {*}
   * @memberof Vector2D
   */
  normalize() {
    return this.scale(1 / this.length);
  }
}

/**
 * 向量的加法
 *
 * @param {*} vec1
 * @param {*} vec2
 * @return {Vector2D}
 */
function vector2dPlus(vec1, vec2) {
  return new Vector2D(vec1.x + vec2.x, vec1.y + vec2.y);
}

/**
 * 向量的減法
 *
 * @param {*} vec1
 * @param {*} vec2
 * @return {Vector2D}
 */
function vector2dMinus(vec1, vec2) {
  return new Vector2D(vec1.x - vec2.x, vec1.y - vec2.y);
}

export {Vector2D, vector2dPlus, vector2dMinus};

感謝各位的閱讀,以上就是“怎么利用Javascript生成平滑曲線”的內容了,經過本文的學習后,相信大家對怎么利用Javascript生成平滑曲線這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永昌县| 秀山| 开鲁县| 安西县| 盖州市| 浠水县| 赫章县| 夹江县| 石阡县| 郑州市| 吉木乃县| 武定县| 建宁县| 阳曲县| 永寿县| 安义县| 祁连县| 婺源县| 卓资县| 资兴市| 定西市| 石台县| 黔江区| 西青区| 化州市| 湾仔区| 准格尔旗| 沂水县| 兖州市| 临朐县| 桂平市| 蒲城县| 自治县| 云南省| 宜都市| 嘉兴市| 永和县| 石泉县| 湘阴县| 晋城| 甘肃省|