Highcharts Tilemap 自动生成正六边形蜂窝状排布算法(Angular)

上一个项目需要用到TileMap,并且给定任意数据,动态排布成正六边形。关于TileMap的排布就是最后数据的结构,我记得当时还在本子上列公式算了一会,算出来个公式。写这篇blog的时候大概过了快一年了,还花了些时间回想。

公式
X代表数据总数,Y代表正六边形正中间那一行有多少个数据,只有当Y为整数的时候,当前的X的数据量才可排布成正六边形。
simple
看圈内就是一个正六边形排布,把数据总数7带入公式,刚好可以得出中间数据个数为3。接下来贴我的代码

this.chartStr = {
      chart: {
        type: 'tilemap',
        inverted: true,
        height: 120,
        width: 200,
        spacingTop: 0,
        spacingRight: 0,
        spacingLeft: 0,
        spacingBottom: 0,
        marginTop: 0,
        marginRight: 40,
        marginLeft:  0,
        marginBottom:  0
      },
      exporting: {
        enabled: false
      },

      credits: {
        enabled: false
      },
      title: {
        useHTML: true,
        text: '<b><span style="color:red">4</span>/37</b>',
        style: {
              'font-size': '1rem',
              'font-weight': 'bold'
            },
        align: 'right',
        verticalAlign: 'bottom',
        y:  -5,
        x:  0
      },
      legend: {
        enabled: false
      },
      xAxis: {
        visible: false
      },

      yAxis: {
        visible: false
      },

      colorAxis: {
        dataClasses: [
              {
                from: 0,
                to: 0,
                color: '#ED1C24',
                name: '1'
              },
              {
                from: 1,
                to: 1,
                color: '#8CC63F',
                name: '2'
              }
            ]
      },

      tooltip: {
        headerFormat: '',
        formatter() {
          if (this.point.value === 0) {
            return '<b>' + this.point.name + ':</b> down';
          } else {
            return '<b>' + this.point.name + ':</b> up';
          }
        }
      },

      plotOptions: {
        series: {
          dataLabels: {
            enabled: false
          },
         
        }
      },

      series: [
        {
          name: '',
          label: {
            enabled: false
          },
          data: []
        }
      ]
    };

const testData = [
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 0
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 0
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 0
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 1
            },
            {
                value: 0
            },
            {
                value: 1
            }
        ];

this.chartStr.series[0].data = this.autoGenerateXandY(testData);
this.chart = new Chart(this.chartStr as any);
    
autoGenerateXandY(datas: any): any {
        let total = datas.length;
        let middleNum = Math.sqrt((total * 4 - 1) / 3);
        if (!/^\d+$/.test(middleNum.toString())) {
            total = this.getNextAmountOfHexHexgon(middleNum);
            middleNum = Math.sqrt((total * 4 - 1) / 3);
        }
        let tileDatas = new Array<TileData>(total);
        let invertedFlag = false;
        let towRowFlag = 0;
        let upRow = (middleNum - 1) / 2;
        let offsetNum = parseInt(((upRow - 1) / 2).toFixed(0));
        let sideNum = (middleNum + 1) / 2;
        for (let i = 0, j = sideNum, z = 0; i < middleNum || z < total; i++) {
            for (let k = offsetNum; k < j + offsetNum; k++, z++) {
                if (z >= datas.length) {
                    continue;
                }
                tileDatas[z] = new TileData();
                tileDatas[z].x = i;
                tileDatas[z].y = k;
                tileDatas[z].value = datas[z].value;
                tileDatas[z].name = datas[z].name;
            }
            if (i === upRow) {
                if (/^\d+$/.test((upRow / 2).toString())) {
                    offsetNum++;
                    towRowFlag = 0;
                } else {
                    towRowFlag = 1;
                }
            } else {
                if (towRowFlag >= 1) {
                    towRowFlag = 0;
                    if (invertedFlag) {
                        offsetNum++;
                    } else {
                        offsetNum--;
                    }
                } else {
                    towRowFlag++;
                }
            }
            invertedFlag = i >= upRow;
            if (invertedFlag) {
                j--;
            } else {
                j++;
            }
        }
        return tileDatas;
    }

    getNextAmountOfHexHexgon(middleNum: number): number {
        middleNum = parseInt(middleNum.toString()) + 1;
        while (!/^\d+$/.test(this.getAmountFromMiddleNum(middleNum).toString())) {
            middleNum++;
        }
        return this.getAmountFromMiddleNum(middleNum);
    }
    
    getAmountFromMiddleNum(middleNum: number): number {
    return (3 * Math.pow(middleNum, 2) + 1) / 4;
  }

这个function返回的数据就可以直接放进highcharts的data里面就可以生成正六边形了,这里还做了处理,当当前数据总数无法构成完美六边形时候,算法会自动找出当前数据总数中下一个可以构成完美六边形的整数,并且使用空数据来填充,可以呈现一个缺失的六边形,但是随着数据增加,六边形可以变成完美状态,当又多一个数据之后,就会进入下一个完美六边形整数形成的缺失六边形。

记录一下算法吧。毕竟当时还是想了一会的,不能白费脑细胞。

后续

这个就是数据不足以形成完美六边形情况下的生成的缺少六边形
在这里插入图片描述
这是数据量刚好可以形成完美六边形的情况
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值