美文网首页Web 前端
zrender 绘制带有中垂线的线段

zrender 绘制带有中垂线的线段

作者: 时光觅迹 | 来源:发表于2021-03-10 14:31 被阅读0次

zrender(Zlevel Render) 是一个轻量级的Canvas类库,绘制直线是很简单的事情,但是绘制中垂线还需借助到数学知识「三角函数」。

            y ^
              |      B(x1, y1)
              |    /┆
              |   / ┆
              |  /  ┆
              | /   ┆
              |/----┼----+ F
              / C \┆
             /|     ┆\D
            / |     ┆
 ——————————/——+—————┼———+————————> x
          /   |O    ┆   E
         /    |     ┆
        /_____|_____┆
    A(x, y)   |      G
              |

设平面坐标系 xoy 中,有线段 A(x, y)、B(x1, y1),计算出垂直于 AB 线段中点 C 上一侧的长度为 n 的线段 CD 两端点坐标

1:算出线段 AB 的长度:

|AB| = \sqrt{(x - x_1)^2 + (y - y_1)^2}

2:算出中点 C 的坐标:

xc = x_1 + \frac{x - x_1}{2}

yc = y_1 + \frac{y - y_1}{2}

3:设过坐标原点 o 的长度为 n 的线段 OE,在 x 轴的正坐标上,则两点坐标为:O(0, 0),E(n, 0)

4:将 OE 平移至 C 点且 O 与 C 重合,得到 CF,则 F 点的坐标为:xf = n + xc; yf = yc

5:运用三角函数,计算各个角的关系

6:线段 AB 与 x 轴的夹角:

\sin(∠BAG) = \sin(∠BCF) = \cos(∠FCD) = \frac{BG}{AB}

\cos(∠BAG) = cos(∠BCF) = \sin(∠FCD) = \frac{AG}{AB}

7:通过三角函数关系可计算,XY平面中一点(x1,y1)经圆周旋转θ角度,得到点(x2,y2),公式如下

x_2 = x_1 \times \cos(θ) - y_1 \times \sin(θ)

y_2 = x_1 \times \sin(θ) + y_1 \times \cos(θ)

8:将 F 点经圆周旋转 ∠FCD 之后,得到 D 点

实现代码:

/**
 * 绊线
 * @ start[ ] 开始点坐标
 * @ end[ ] 结束点坐标
 * @ direction[ ] 箭头方向坐标(以一个点的坐标代替)
 * @ around `(String)` 箭头包围方式:single - 单侧;double - 双侧
 *
 * @return
 * 当 around 为 "single" 时,在绘制结束后,取 `shape.site` 可得到箭头在线段的哪一侧;
 * 返回 "left" 或 "right"
 */
var TripWire = zrender.Path.extend({
  type: 'TripWire',
  shape: {
    start: [0, 0],
    end: [0, 0],
    direction: [0, 0],
    around: 'single', // or double, set the arrow site, single is left or right on the line, double is on the left and right
    side: null
  },
  style: {
    stroke: '#000',
    fill: 'rgba(0, 0, 255, 0.2)'
  },
  buildPath: function (ctx, shape) {
    // 起点坐标
    var x1 = shape.start[0]
    var y1 = shape.start[1]
    // 终点坐标
    var x2 = shape.end[0]
    var y2 = shape.end[1]
    // 方向坐标(单向有效)
    var x3 = shape.direction[0]
    var y3 = shape.direction[1]

    ctx.moveTo(x1, y1)
    ctx.lineTo(x2, y2)

    // console.log("线段坐标", x1, y1, x2, y2)
    console.log(shape.side)
    if (!shape.side || shape.side == null || (shape.side !== 'left' && shape.side !== 'right')) {
      // 计算箭头所在线段哪一侧,先计算一个点相对线段的向量,>0 左侧,<0 右侧,=0 线上
      var vector = (x1 - x3) * (y2 - y3) - (y1 - y3) * (x2 - x3)
      console.log(vector)

      // 返回箭头所在方位
      if (shape.around === 'single') {
        if (vector < 0) {
          shape.site = 'left'
        } else {
          shape.site = 'right'
        }
      }
    } else {
      shape.site = shape.side
    }
    // console.log("向量", vector);

    // TODO
    /**
         *            y ^
         *              |      B(x1, y1)
         *              |    /┆
         *              |   / ┆
         *              |  /  ┆
         *              | /   ┆
         *              |/----┼----+ F
         *              / C \┆
         *             /|     ┆\D
         *            / |     ┆
         * ——————————/——+—————┼———+————————> x
         *          /   |O    ┆   E
         *         /    |     ┆
         *        /_____|_____┆
         *    A(x, y)   |      G
         *              |
         *
         * 设平面坐标系 xoy 中,有线段 A(x, y)、B(x1, y1),计算出垂直于 AB 线段中点 C 上一侧的长度为 n 的线段 CD 两端点坐标
         *                              _________________________
         * 1:算出线段 AB 的长度:|AB| = √ (x - x1)^2 + (y - y1)^2
         *
         * 2:算出中点 C 的坐标:xc = x1 + (x - x1) / 2;  yc = y1 + (y - y1) / 2
         *
         * 3:设过坐标原点 o 的长度为 n 的线段 OE,在 x 轴的正坐标上,则两点坐标为:O(0, 0),E(n, 0)
         *
         * 4:将 OE 平移至 C 点且 O 与 C 重合,得到 CF,则 F 点的坐标为:xf = n + xc;   yf = yc
         *
         * 5:运用三角函数,计算各个角的关系
         *
         * 6:线段 AB 与 x 轴的夹角:sin(∠BAG) = sin(∠BCF) = cos(∠FCD) = BG/AB;
         *                          cos(∠BAG) = cos(∠BCF) = sin(∠FCD) = AG/AB
         *
         * 7:通过三角函数关系可计算,XY平面中一点(x1,y1)经圆周旋转θ角度,得到点(x2,y2),公式如下
         *          x2 = x1 * cos(θ) - y1 * sin(θ)
         *          y2 = x1 * sin(θ) + y1 * cos(θ)
         *
         * 8:将 F 点经圆周旋转 ∠FCD 之后,得到 D 点
         *
         */

    // 线段长度
    var ABLength = Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2)))
    // console.log("leng", ABLength);

    // 线段中心点
    var center_x1 = (x2 - x1) / 2 + x1
    var center_y1 = (y2 - y1) / 2 + y1
    // console.log("center", center_x1, center_y1);

    // 设长度为100px的线段 OE 经过坐标原点,且 O 点在坐标原点上,E点在 x 轴正坐标上,则这两点的坐标如下
    var xO = 0
    var yO = 0
    var xE = 20
    var yE = 0

    // 箭头两个角的点:a, b
    /**
         *    ∧
         *  a/__\ b
         *    ||
         *    ||
         */
    var xArrowAngle_1 = xE - 5
    var yArrowAngle_1 = yE + 3
    var xArrowAngle_2 = xE - 5
    var yArrowAngle_2 = yE - 3

    // 设线段为直角三角形的斜边,则两条直角边长度为
    var xLength = Math.abs(x1 - x2)
    var yLength = Math.abs(y1 - y2)
    // console.log("边长", xLength, yLength);

    // 判断线段方向【注意,在此处坐标系为屏幕坐标系,Y 轴应与普通平面坐标系反向,即从上到下为正向】
    // 计算线段与 x 轴的夹角三角函数
    var cosAngle = yLength / ABLength
    var sinAngle = xLength / ABLength
    if (x1 < x2 && y1 < y2) {
    // 左上到右下 ↘
      sinAngle = -sinAngle
    } else if (x1 > x2 && y1 > y2) {
    // 右下到左上 ↖
      sinAngle = -sinAngle
    } else if (x1 < x2 && y1 > y2) {
    // 左下到右上 ↗
    } else if (x1 > x2 && y1 < y2) {
    // 右上到左下 ↙
    }
    // console.log("三角", cosAngle, sinAngle);

    // 将 OE 进行旋转得到 OF
    var xF = xE * cosAngle - yE * sinAngle
    var yF = xE * sinAngle + yE * cosAngle

    // 旋转箭头的两个角
    var xArrowAngle_1_B = xArrowAngle_1 * cosAngle - yArrowAngle_1 * sinAngle
    var yArrowAngle_1_B = xArrowAngle_1 * sinAngle + yArrowAngle_1 * cosAngle
    var xArrowAngle_2_B = xArrowAngle_2 * cosAngle - yArrowAngle_2 * sinAngle
    var yArrowAngle_2_B = xArrowAngle_2 * sinAngle + yArrowAngle_2 * cosAngle
    // console.log("旋转", xO, yO, xF, yF);

    // 将 OF 平移至线段中心点,且 O 点与中心点重合,则新的线段坐标如下
    xO = center_x1
    yO = center_y1
    xF = center_x1 + xF
    yF = center_y1 + yF
    // console.log("move OE", xO, yO, xF, yF);

    // 平移箭头的两个角
    xArrowAngle_1_B = center_x1 + xArrowAngle_1_B
    yArrowAngle_1_B = center_y1 + yArrowAngle_1_B
    xArrowAngle_2_B = center_x1 + xArrowAngle_2_B
    yArrowAngle_2_B = center_y1 + yArrowAngle_2_B

    if (shape.around === 'double') {
    // 双向画线,先画第一侧
      ctx.moveTo(xO, yO)
      ctx.lineTo(xF, yF)
      ctx.lineTo(xArrowAngle_1_B, yArrowAngle_1_B)
      ctx.lineTo(xArrowAngle_2_B, yArrowAngle_2_B)
      ctx.lineTo(xF, yF)
    }

    // 计算 F 点在线段的哪一侧,是否和光标点击点位于同一侧,或者双向画箭头
    var vectorF = (x1 - xF) * (y2 - yF) - (y1 - yF) * (x2 - xF)
    // if ((vector > 0 && vectorF < 0) || (vector < 0 && vectorF > 0) || shape.around == "double") {
    if ((shape.site === 'right' && vectorF < 0) || 
        (shape.site === 'left' && vectorF > 0) || 
        shape.around === 'double') {
    // 不同侧,旋转Angle之后再旋转180°
      xF = xE * cosAngle - yE * sinAngle
      yF = xE * sinAngle + yE * cosAngle

      // 因为 sin(180) = 0; cos(180) = -1; 所以此处直接简写
      xF = -xF
      yF = -yF

      xF = center_x1 + xF
      yF = center_y1 + yF

      // 旋转箭头的两个角
      xArrowAngle_1_B = xArrowAngle_1 * cosAngle - yArrowAngle_1 * sinAngle
      yArrowAngle_1_B = xArrowAngle_1 * sinAngle + yArrowAngle_1 * cosAngle
      xArrowAngle_2_B = xArrowAngle_2 * cosAngle - yArrowAngle_2 * sinAngle
      yArrowAngle_2_B = xArrowAngle_2 * sinAngle + yArrowAngle_2 * cosAngle
      xArrowAngle_1_B = center_x1 - xArrowAngle_1_B
      yArrowAngle_1_B = center_y1 - yArrowAngle_1_B
      xArrowAngle_2_B = center_x1 - xArrowAngle_2_B
      yArrowAngle_2_B = center_y1 - yArrowAngle_2_B
    }

    // 画线
    ctx.moveTo(xO, yO)
    ctx.lineTo(xF, yF)
    ctx.lineTo(xArrowAngle_1_B, yArrowAngle_1_B)
    ctx.lineTo(xArrowAngle_2_B, yArrowAngle_2_B)
    ctx.lineTo(xF, yF)
  }
})

相关文章

  • zrender 绘制带有中垂线的线段

    zrender(Zlevel Render) 是一个轻量级的Canvas类库,绘制直线是很简单的事情,但是绘制中垂...

  • 43. 线段绘制

    本文解释线段绘制,并通过线段绘制出三角形 线段与线段构成的三角形如下:

  • PS中如何使用标尺和直线工具进行骨科测量

    概述 骨科影像检查资料(如X片、CT片)在进行测量里,常需要用到画线、取线段中点、画已知线段中垂线、测量角度等,可...

  • 欧几里德ios4.07版系列之三,中垂线系列

    话不多说,直接上图: 中垂线系列的主要图片如下 分别以线段两个端点为圆心,以线段长度为半径画圆,两个圆的交点可以确...

  • Java Grapgics

    绘制线段和文字

  • OpenCV基本绘图函数

    线段:line 函数 img: 要绘制线段的图像。 pt1: 线段的起点。 pt2: 线段的终点。 color: ...

  • 绘制线段

    绘制线段 [self setNeedsDisplay]该方法可调用drawRect - (void)drawRec...

  • 绘制线段

    绘制线段步骤 新建一个类,继承自UIView(略) 在-(void)drawRect:(CGRect)rect方法...

  • OpenGL中一种高效的线段反走样技术

    令人讨厌的“走样” 我在日常工作中通过传统的OpenGL绘制函数绘制线段时,发现绘制出的线段边缘充满了“锯...

  • 使用ZRender绘制表格

    公司医院项目,需要定制化显示病人的体温、脉搏、呼吸。由于界面是定义好的,无法使用开源项目来改,干脆自己画一个了。 ...

网友评论

    本文标题:zrender 绘制带有中垂线的线段

    本文链接:https://www.haomeiwen.com/subject/nlwaoktx.html