可视化学习:利用向量判断多边形边界

文章讲述了如何在Canvas2D环境中,利用向量和三角剖分技术判断鼠标位置,实现在多边形内部改变填充颜色的功能。作者提到内置的`isPointInPath`方法局限性,随后自定义了一个方法解决这个问题,但指出这种方法存在性能开销和通用性问题。
摘要由CSDN通过智能技术生成

引言

继续巩固我的可视化学习,向量运算是计算机图形学的基础,本例依旧是向量的一种应用,利用向量判断多边形边界,但是多边形的边界判断稍微有点复杂,所以除了应用向量之外,还需要借助三角剖分的相关工具。这个例子中可视化的展示采用Canvas2D来实现。

问题

假设Canvas画布上存在一个如下多边形:

我们移动鼠标的时候,想要实现一个效果,就是当鼠标移动到多边形内部的时候,将多边形内部的填充颜色更新成其他颜色;所以此时我们需要判断鼠标是否在多边形内部,这就涉及到多边形边界的判断。

思路

首先我们先将这个多边形绘制到Canvas画布上。

<canvas width="512" height="512"></canvas>
canvas {
  width: 512px;
  height: 512px;
  border: 1px solid #eee;
}
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(1, -1);

const vertices = [
    [ -179.2, 128 ],
    [ -102.4, 76.8 ],
    [ -64, 181.76 ],
    [ -25.6, 143.36 ],
    [ -25.6, 33.28 ],
    [ 102.4, 53.76 ],
    [ 0, -153.6 ],
    [ -76.8, -76.8 ],
    [ -153.6, -76.8 ],
    [ -115.2, 0 ]
];

drawPolygon(vertices);

function drawPolygon(vertices, fillStyle = "red") {
  ctx.beginPath();
  ctx.moveTo(...vertices[0]);
  for (let i = 1; i < vertices.length; i ++) {
      ctx.lineTo(...vertices[i]);
  }
  ctx.closePath();
  ctx.fillStyle = fillStyle;
  ctx.fill();
}
1. 调用API

对于Canvas2D而言,有一个API自带的方法,就是CanvasRenderingContext2D的isPointInPath方法。

这个方法使用起来非常简单,我们在这个时候直接增加一个鼠标移动事件的监听就可以。

const {left, top} = canvas.getBoundingClientRect();
canvas.addEventListener('mousemove', e => {
  const {x: pageX, y: pageY} = e;
  // 坐标转化
  const offsetX = x - left;
  const offsetY = y - top;
  // 清除画布
  ctx.clearRect(-256, -256, 512, 512);
  if (ctx.isPointInPath(offsetX, offsetY)) {
    drawPolygon(vetices, "green");
  } else {
    drawPolygon(vetices);
  }
});

但是这个API的使用存在很大的局限性,就是它只能针对当前绘制的图形生效。

就比如说,如果在完成这个多边形的绘制之后,又绘制了一个小三角形。

const triangle = [
  [100, 100], 
  [100, 200], 
  [150, 200]
];

drawPolygon(triangle, "blue");

为了保持这个小三角形,我们还需要修改鼠标监听事件,以达到更新画布时,三角形依旧被绘制。

canvas.addEventListener('mousemove', e => {
  const {pageX: x, pageY: y} = e;
  // 坐标转化
  const offsetX = x - left;
  const offsetY = y - top;
  // 清除画布
  ctx.clearRect(-256, -256, 512, 512);
  if (ctx.isPointInPath(offsetX, offsetY)) {
    drawPolygon(vertices, "green");
    drawPolygon(triangle, "blue");
  } else {
    drawPolygon(vertices);
    drawPolygon(triangle, "blue");
  }
});

此时我们再移动鼠标,就会发现,在鼠标移动到多边形内部时,多边形的填充颜色并不会变,但是当鼠标移动到小三角形内部时,多边形的填充色发生了变化;这就是Canvas2D Context的isPointInPath方法所存在的局限性。

2. 自定义isPointInPath

为了突破Canvas2D API中自带方法的局限性,最简单的方法就是,我们手动自定义一个自己的isPointInPath方法。

具体实现如下:

function isPointInPath(x, y) {
  // 根据ctx重新clone一个新的Canvas对象
  const cloned =  ctx.canvas.cloneNode().getContext('2d');
  cloned.translate(canvas.width / 2, canvas.height / 2);
  cloned.scale(1, -1);
  let ret = false;
  // 绘制多边形,判断点是否在图形内部
  drawPolygon(cloned, vertices, "red");
  ret |= cloned.isPointInPath(x, y);
  if (!ret) {
    // 如果不在,继续绘制小三角形,判断点是否在图形内部
    drawPolygon(cloned, triangle, "blue");
    ret |= cloned.isPointInPath(x, y);
  }
  return ret;
}
  • 首先,根据原画布的Context创建一个新的Canvas对象并获取它的上下文
  • 然后绘制多边形,并判断鼠标是否在多边形内部
  • 如果不在多边形内部,继续判断是否在三角形内部
  • 最后将结果返回

可以看到,在这个自定义的方法内部,我们依然是调用了Canvas2D Context的isPointInPath方法。

接着我们还需要修改鼠标的监听事件,把判断方法改为我们自定义的isPointInPath。

此时移动鼠标,可以看到,当鼠标移动到多边形或者三角形内部,都可以使多边形的填充色发生变化;这就是因为我们在自定义的isPointInPath中做的两次判断。

但是我们也能发现,虽然这种方式解决了我们在第一种方式中所碰到的问题,却也存在其他问题,第一,是增加了很多无谓的Canvas绘图操作;第二,是通用性差,如果图形有修改,那么isPointInPath方法就要跟着修改,并且这个方法依赖于Canvas2D的API,如果哪天修改了绘图方式,比如改为使用WebGL,就不能使用了。

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值