二阶贝塞尔曲线生成弧线

2024-01-14 10:13:48 浏览数 (1)

概述

本文分享一个二阶贝塞尔曲线曲线生成弧线的算法。

效果

实现

1. 封装方法

代码语言:javascript复制
class ArcLine {
  constructor(from, to, num = 100) {
    this.from = from;
    this.to = to;
    this.num = num;
    return this.getPointList();
  }
  getPointList() {
    const { from, to } = this
    const ctrlPoint = this.getOffsetPoint(from, to);
    const points = this.create2PBezier(from, ctrlPoint, to)
    return points
  }
  getOffsetPoint(start, end) {
    const distance = this.getDistance(start, end) / 2; //除以3?
    let angle, dX, dY;
    const mp = [start[0], start[1]];
    const deltaAngle = - Math.PI / 8; //偏移0.2弧度
    if (start[0] != end[0] && start[1] != end[1]) { //斜率存在
      const k = (end[1] - start[1]) / (end[0] - start[0]);
      angle = Math.atan(k);
    } else if (start[0] == end[0]) { //垂直线
      angle = (start[1] <= end[1] ? 1 : -1) * Math.PI / 2;
    } else { //水平线
      angle = 0;
    }
    if (start[0] <= end[0]) {
      angle -= deltaAngle;
      dX = Math.round(Math.cos(angle) * distance);
      dY = Math.round(Math.sin(angle) * distance);
      mp[0]  = dX;
      mp[1]  = dY;
    } else {
      angle  = deltaAngle;
      dX = Math.round(Math.cos(angle) * distance);
      dY = Math.round(Math.sin(angle) * distance);
      mp[0] -= dX;
      mp[1] -= dY;
    }
    return mp;
  }

  getDistance(p1, p2) {
    return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0])   (p1[1] - p2[1]) * (p1[1] - p2[1]));
  }

  bezier2P(p0, p1, p2, t) {
    const P0 = p0 * Math.pow(1 - t, 2);
    const P1 = p1 * 2 * t * (1 - t);
    const P2 = p2 * t * t;
    return P0   P1   P2;
  }

  getBezierNowPoint2P(p0, p1, p2, num, tick) {
    return {
      x: this.bezier2P(p0[0], p1[0], p2[0], num * tick),
      y: this.bezier2P(p0[1], p1[1], p2[1], num * tick),
    };
  }
  create2PBezier(p0, p1, p2) {
    const num = this.num
    const t = 1 / (num - 1);
    const points = [];
    for (let i = 0; i < num; i  ) {
      const point = this.getBezierNowPoint2P(p0, p1, p2, i, t);
      points.push([point.x, point.y]);
    }
    return points;
  }
}

2. 前端调用

示例使用openlayers实现。

代码语言:javascript复制
let vetSource = new ol.source.Vector({
  features: [],
});
let vectorLayer = new ol.layer.Vector({
  source: vetSource,
  style: new ol.style.Style({
    stroke: new ol.style.Stroke({
      color: "#f00",
      width: 2,
    }),
  }),
});
map.addLayer(vectorLayer)

function addAllLines() {
  let features = [];
  for (let i = 0; i < pointData.length; i  ) {
    const after = pointData[i];
    const from = [101.797439042302, 36.5937248286007];
    const to = [after.lon, after.lat].map(Number);
    let points = new ArcLine(from, to);
    points = points.map((p) => ol.proj.fromLonLat(p));
    lineData.push(points);

    features.push(
      new ol.Feature({
        geometry: new ol.geom.LineString(points),
      })
    );
  }
  vetSource.addFeatures(features);
  map.getView().animate({
    center: [12474607.173951693, 4278483.982819865],
    zoom: 3.8,
  });
}

0 人点赞