上一个项目需要用到TileMap,并且给定任意数据,动态排布成正六边形。关于TileMap的排布就是最后数据的结构,我记得当时还在本子上列公式算了一会,算出来个公式。写这篇blog的时候大概过了快一年了,还花了些时间回想。
X代表数据总数,Y代表正六边形正中间那一行有多少个数据,只有当Y为整数的时候,当前的X的数据量才可排布成正六边形。
看圈内就是一个正六边形排布,把数据总数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里面就可以生成正六边形了,这里还做了处理,当当前数据总数无法构成完美六边形时候,算法会自动找出当前数据总数中下一个可以构成完美六边形的整数,并且使用空数据来填充,可以呈现一个缺失的六边形,但是随着数据增加,六边形可以变成完美状态,当又多一个数据之后,就会进入下一个完美六边形整数形成的缺失六边形。
记录一下算法吧。毕竟当时还是想了一会的,不能白费脑细胞。
后续
这个就是数据不足以形成完美六边形情况下的生成的缺少六边形
这是数据量刚好可以形成完美六边形的情况