【创新实训 - 个人报告】07 - 层次结构:多边形融合算法

开始之前

在本周进行开发时,我们遇到了非常有意思的一个需求。
对文章进行层次化处理,需要把字按照词、句子、段落、文章、书籍的顺序组合起来。每个字都有一个多边形,因此词、句子、段落、文章、书籍就需要把这些多边形组合起来显示。如何写这个算法是重中之重。

设计思路

多边形的存储方式是一个有序点数组。例如:
[[0, 0], [0, 1], [1, 1], [1, 0]]
如果这时又来了一个多边形,要和上面组合呢?
[[0, 0], [0, 1], [-1, 1], [-1, 0]]

我们需要剔除公共边:
[[0, 0], [0, 1], [1, 1], [1, 0]]
[[0, 0], [0, 1], [-1, 1], [-1, 0]]
然后将其组合起来。
[[-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [0, 0]]

你发现了什么?

  • 由于第二个多边形的记录方向和第一个相反,其中一个多边形数组被反转了。
  • 从公共边开始,将多边形数组拆分开,再匹配端点,连起来。

感觉很简单,不是嘛?但是你还需要考虑这些部分:

  • 多边形可能是重叠的。
  • 一条公共边可能包含多个点。
  • 两个复杂多边形可能有一条以上的公共边,这会导致中间出现空洞。
  • 在这个项目中,多边形可能产生跨页。

笔者在这里实现了一个算法,当然它也是有缺陷的,在两个复杂多边形有一条以上的公共边时,我会简单的把中间的空洞删除(因为我的引擎渲染器为了省事,没有给出画洞的功能,哈哈哈。)有兴趣的同学还可以多多探讨。

这里先用BFS标记出所有能融合在一起的多边形组合,然后依次对多边形组进行融合操作。

// --------------------------------------------------------------------------------
PolygonGroup.prototype.isLine = function(dot1, dot2, points){
  let a = points.indexOf(dot1);
  let b = points.indexOf(dot2);
  if(a < 0 || b < 0) return false;
  if((a === 0 && b === points.length - 1) || (a === points.length - 1 && b === 0)) return true;
  return Math.abs(a - b) === 1;
}
PolygonGroup.prototype.hasCommonLine = function(points_1, points_2){
  let a, b;
  for(let i = 0; i < points_1.length; i++){
    if (i === points_1.length - 1) {
      a = i; b = 0;
    }else{
      a = i; b = i + 1;
    }
    if(this.isLine(points_1[a], points_1[b], points_2)){
      return true;
    }
  }
  return false;
}
PolygonGroup.prototype.getGroupBFS = function(map){
  let visited = new Array(map.length);
  for(let i = 0; i < visited.length; i++) {
    visited[i] = false;
  }
  let queue = [];
  let polygons = [];
  for(let i = 0; i < visited.length; i++){
    if(!visited[i]){
      polygons.push([]);
      queue.push(i);
      visited[i] = true;
      while(queue.length !== 0) {
        let target = queue.shift();
        polygons[polygons.length - 1].push(target);
        for (let j = 0; j < map[target].length; j++) {
          if (map[target][j] && !visited[j]) {
            queue.push(j);
            visited[j] = true;
          }
        }
      }
    }
  }
  return polygons;
}
PolygonGroup.prototype.mergePolygon = function(polygon_1, polygon_2){
  let common_dots = [];
  let start_dot = null;
  for(let i = 0; i < polygon_1.length; i++) {
    if(polygon_2.indexOf(polygon_1[i]) !== -1){
      common_dots.push(polygon_1[i]);
    }else{
      start_dot = polygon_1[i];
    }
  }
  let polygon = [start_dot];
  let index = polygon_1.indexOf(start_dot);
  let now = 1;
  let scan = 1;
  let target;
  while(true){
    index += scan;
    if(now === 1) {
      if(index < 0) index = polygon_1.length - 1;
      if(index > polygon_1.length - 1) index = 0;
      target = polygon_1[index];
      if(target === start_dot) break;
      if(common_dots.indexOf(target) === -1){
        polygon.push(target);
      }else{
        polygon.push(target);
        now = 2;
        index = polygon_2.indexOf(target);
        scan = common_dots.indexOf(polygon_2[((index + 1) % polygon_2.length)]) === -1 ? 1 : -1;
      }
    }else if(now === 2) {
      if(index < 0) index = polygon_2.length - 1;
      if(index > polygon_2.length - 1) index = 0;
      target = polygon_2[index];
      if(target === start_dot) break;
      if(common_dots.indexOf(target) === -1){
        polygon.push(target);
      }else{
        polygon.push(target);
        now = 1;
        index = polygon_1.indexOf(target);
        scan = common_dots.indexOf(polygon_1[((index + 1) % polygon_1.length)]) === -1 ? 1 : -1;
      }
    }
  }
  return polygon;
}
PolygonGroup.prototype.mergePoints = function(points){
  let polygon_map = [];
  for(let i = 0; i < points.length; i++) {
    polygon_map.push(new Array(points.length));
  }
  for(let i = 0; i < polygon_map.length; i++) {
    for (let j = 0; j < polygon_map[i].length; j++) {
      polygon_map[i][j] = false;
    }
  }
  for(let i = 0; i < points.length; i++) {
    for (let j = i + 1; j < points.length; j++) {
      if (this.hasCommonLine(points[i], points[j])){
        polygon_map[i][j] = true;
        polygon_map[j][i] = true;
      }
    }
  }
  let polygon_group = this.getGroupBFS(polygon_map);
  let points_group = [];
  for(let i = 0; i < polygon_group.length; i++){
    points_group.push(points[polygon_group[i].splice(0, 1)]);
    while(polygon_group[i].length > 0){
      for(let j = 0; j < polygon_group[i].length; j++){
        if(this.hasCommonLine(points_group[i], points[polygon_group[i][j]])){
          points_group[i] = this.mergePolygon(points_group[i], points[polygon_group[i].splice(j, 1)]);
          break;
        }
      }
    }
  }
  return points_group;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值