当前位置: 移动技术网 > IT编程>网页制作>Html5 > canvas里面如何基于随机点绘制一个多边形的方法

canvas里面如何基于随机点绘制一个多边形的方法

2019年07月25日  | 移动技术网IT编程  | 我要评论

起因

今天在学习《html5+javascript动画基础》这本书的时候,在第八章的第三节讲到如何用三个弹簧连接三个点来做拉伸运动。

在做完例子之后,就想到如果是四个点,五个点,怎么样。

就改写了一下代码,把点的数目变量化。最终的效果是能实现各个点最终的拉伸运动到平衡,可是点之间的连线不是很好看,有些是交叉的。

于是就想着能不能优化这一块。

旋转连线

前面例子里面的点,都是随机位置,所以连线不可控。所以想先从这块着手。

先以某一个点为参照点,获得其他点相对于这个点的角度。

然后按照角度从小到大的去连接这些点,这样就能画出一个正常的多边形了。

大致实现代码如下:

let balls = [];
let ballnum = 6;
let firstball = null;
while(ballnum--) {
  let ball = new ball(20, parsecolor(math.random() * 0xffffff))
  ball.x = math.random() * width;
  ball.y = math.random() * height;
  balls.push(ball)

  if (!firstball) {
    firstball = ball
    ball.angle = 0
  } else {
    const dx = ball.x - firstball.x,
          dy = ball.y - firstball.y;

    ball.angle = math.atan2(dy, dx);
  }
}

// 尝试让球连线是一个正多边形
balls = balls.sort((balla, ballb) => {
  return balla.angle - ballb.angle
})

这样在最后绘制连线的时候,遍历数组就能按照角度从小到大来绘制了。

效果如下:

这样是能极大的减少交叉线的情况,可还是无法完全避免。

接下来,想尝试优化这个方案,比如angle用math.abs来取正,或者每一个点都找夹角最小的点来连线。可是结果都不行,无法避免交叉线。

基于中心点旋转

后面又想到一个思路,如果能确定多边形的中心点,那么分别计算所有点相对于中心点的夹角,就能以顺时针或者逆时针来连接这些点。

可是在网上找了半天,所有点算法里面,都是要求有一系列按某个时针顺序排列的点。

可是如果我有这些点,就已经能绘制多边形了。只好放弃

x轴两极点分割

无奈之下只好找google,然后就发现了知乎上的一个答案挺好的:

具体算法描述,大家看那个答案就好,我就不赘述了。

不过在连接上链和下链的时候,其实只要保证上链是x轴降序连接,下链是x轴升序连接即可(以逆时针方向绘制)。至于x轴相同的点,不管是优先y轴大的还是小的都可以。

实现的时候,是严格按照答案里面的算法实现的。

在判断一个点是属于上链还是下链的时候,一开始想的是基于两点确定直线的函数方程,再引入点的坐标来计算。不过后面想到,所有的点都以最左边的极点来计算斜角,然后根据角度大小来划分,视觉上更好理解。

大致代码如下:

let balls = [];
let tempballs = [];
let ballnum = 6;
let isdragingball = false;

while(ballnum--) {
  let ball = new ball(10, parsecolor(math.random() * 0xffffff))
  ball.x = math.random() * width;
  ball.y = math.random() * height;
  tempballs.push(ball)
}

// 让点按x轴升序排序
tempballs = tempballs.sort((balla, ballb) => {
  return balla.x - ballb.x
})

// 找x轴左右极点
let firstball = tempballs[0],
    lastball = tempballs[tempballs.length -1];
let smallxballs = tempballs.filter(ball => ball.x === firstball.x),
    bigxballs = tempballs.filter(ball => ball.x === lastball.x)

// 处理左右极点有多个的情况
if (smallxballs.length > 1) {
  smallxballs.sort((balla, ballb) => {
    return ballb.y - balla.y
  })
}
if (bigxballs.length > 1) {
  bigxballs.sort((balla, ballb) => {
    return ballb.y - balla.y
  })
}

firstball = smallxballs[0]
lastball = bigxballs[0]

// 获得极点连线的角度
let splitlineangle = math.atan2(lastball.y - firstball.y, lastball.x - firstball.x);
let upperballs = [],
    lowerballs = [];

// 所有其他点跟firstball计算角度
// 大于splitlineangle的都是下链
// 其他是上链
tempballs.foreach(ball => {
  if (ball === firstball || ball === lastball) {
    return false
  }
  let angle = math.atan2(ball.y - firstball.y, ball.x - firstball.x);
  if (angle > splitlineangle) {
    lowerballs.push(ball)
  } else {
    upperballs.push(ball)
  }
})

// 处理x轴相同情况的排序
lowerballs = lowerballs.sort((balla, ballb) => {
  if (balla.x !== ballb.x) {
    return balla.x - ballb.x
  }
  return ballb.y - balla.y
})

upperballs = upperballs.sort((balla, ballb) => {
  if (balla.x !== ballb.x) {
    return ballb.x - balla.x
  }
  return ballb.y - ballb.x
})

// 逆时针连接所有的点
balls = [firstball].concat(lowerballs, [lastball], upperballs)

balls = balls.map((ball, i) => {
  ball.text = i + 1;
  return ball
})

最终返回的balls,就是按逆时针排序的多边形的点了。

效果如下:

各个球的内部状态如下:

 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网