数学正余弦应用——创建六边形环或者六边形面盘

如下图:

其中的小六边形可以用一张图片替换,也可以用代码直接绘制,这里就不说具体绘制小六边形了,上面的数字是调试用的,可以忽略。 

要产生一个这样的图形,有三种方式。

1、按列遍历生成,分左边,右边两部分,每部分按照对应的列产生对应的六边形,再分析每列小六边形个数规律,每列小六边形个数一次递增1或者一次递减1,这样的递减导致小六边形的位置变化,不能简单地按照行列索引去设置位置,对每列来说,水平方向的位置都是不变的,唯一需要调整的是竖直方向的位置,这看起来比较麻烦,但是也有规律的,仔细找能找到,这里不用这种方式

2、如上图,可以先生成三条蓝色线上的小六边形,因为三条蓝色线的是小六边形都与最中心的小六边形相连接,小六边形的坐标位置算起来很方便,直接通过遍历索引乘以对应的小六边形宽高,当然这里的宽不是真正的宽,因为斜线上的小六边形是斜着的,相对坐标轴来说两个小六边形的距离就不是它们的宽了,但是可以在编辑器上计算出来,这个值是固定的。三条蓝色线上的小六边形生成好后,就可以按区域(上图中①②③④⑤⑥)生成各自区域的小六边形了,这里又回到1了,分析每个区域的列数,按照列数产生各列的小六边形,这里也不用这种方式,这里要用的是下面的方式。 

3、按环生成,先生成一环,然后多次生成,就生成多个环了,拼起来就是一个盘面,如下图。

上图共7环,实际上是8环,中间还有一个,也算一环。

用正余弦来确定各区域小六边形的位置坐标,如图:

 对每一环来说,环上的小六边形的个数是确定的,每增加一环,小六边形的个数增加6个,每个小六边形相对于坐标原点的角度也是确定的(360/小六边形的个数),小六边形的位置则为(x=R * cos(a),y=R * sin(a));但是R的值是变化的,因为环并不是一个标准的圆,关键在于求出R的值,为了方便计算,将坐标系逆时针旋转30度,以上图中最外环为例,最外环的六边形42的角度为0(360),顺时针旋转,则六边形7的角度则为60,六边形14的角度为120,六边形21的角度为180,六边形28的角度为240,六边形35的角度为300。

还是以最外环为例,定义两个小六边形的高差为h,宽差为w,最外环有42个六边形,每相邻两个六边形之间的角度差为8.571428571428571(360/42),保留两位小数取8.57;

从起始点六边形42开始,分析各个小六边形的位置:

六边形42的位置为(R * cos(0),R * sin(0));

则六边形1-6的位置为(R * cos(0),R * sin(0)+i * h),i为六边形1-六边形6的索引(1,2,3,4,5,6);

六边形7的位置为(R * cos(60),R * sin(60));

六边形8-13的位置为(R * cos(60)-i*w,R * sin(60)+i * h),i为六边形8-六边形13的索引(1,2,3,4,5,6);

六边形14的位置为(R * cos(120),R * sin(120));

六边形15-20的位置为(R * cos(120)-i*w,R * sin(60)-i * h),i为六边形15-六边形20的索引(1,2,3,4,5,6);

六边形21的位置为(R * cos(180),R * sin(180));

六边形22-27的位置为(R * cos(180),R * sin(180)-i * h),i为六边形15-六边形20的索引(1,2,3,4,5,6);

六边形28的位置为(R * cos(240),R * sin(240));

六边形29-34的位置为(R * cos(240)+i*w,R * sin(240)-i * h),i为六边形29-六边形34的索引(1,2,3,4,5,6);

六边形35的位置为(R * cos(360),R * sin(360));

六边形36-41的位置为(R * cos(360)+i*w,R * sin(360)-i * h);

每个小六边形的位置都能方便的表示出来。这里为了分析清楚过程,写了很多步,但是代码里只需一个for循环。

上面的类似R * cos(a)+-i*w,R * sin(a)+-i * h,具体是加还是减跟坐标系的类型有关,要看坐标系的原点是在左下角还是左上角,上面是以laya的坐标系来的。

就分析到这里,下面是具体代码(TypeScript),小六边形用的一个预制体表示:

先定义一个常量类:

export default class Constants {

    public static readonly Row:number = 13;

    public static readonly Col:number = 9;

   //定义小六边形的宽度

    public static readonly HexItemWidth:number = 74;

    //定义小六边形的高度

    public static readonly HexItemHeight:number = 64;

    //定义小六边形的半宽度

    public static readonly HalfHexItemWidth:number = 37;

    //定义小六边形的高度

    public static readonly HalfHexItemHeight:number = 32;

    //定义相邻两小六边形的宽度间隔(就是上面分析说的宽度差)_

    public static readonly HexItemGap:number = 55;

    //定义边界角度

    public static readonly RightBottomAngle:number = 60;

    public static readonly BottomAngle:number = 120;

    public static readonly LeftBottomAngle:number = 180;

    public static readonly LeftAngle:number = 240;

    public static readonly LeftTopAngle:number = 300;

    public static readonly RightTopAngle:number = 360;

    public static readonly HexItemOffsetAngle:number = 30;

}

由于在进行sin,cos运算时,会产生一个小数,再乘以一个R后,会产生一个误差,表现是相邻两小六边形中间会出现一个小缝隙(一个环中会出现2-4个),主要是因为sin,cos那个小数产生的,所以保留两位小数予以解决,系统提供的接口保留两位小数的方法会进行四舍五入,所以定义一个数学类,主要用于保留两位小数,不进行四舍五入:

export default class MathUtils {

    /**

     * @param num 保留两位数,不进行四舍五人

     * @returns

     */

    public static to2Fixed(num:number):number{

        if(num >= 0) return Math.floor(num*100)/100;

        return Math.ceil(num*100)/100;

    }

    public static to2FixedOther(num:number):number{

       return Number(num.toFixed(3).slice(0, -1));

    }

}

六边形面盘,就是生成多个六边形环:

/**

     * 六边形面盘

     * @param round 该面盘由多少环组成

     */

    private createHexagonDish(round:number = 1):void{

        for(let i:number = 1;i<=round;i++){

            this.createHexagonCircle(i);

        }

    }

生成某一个环:

/**

     * 六边形环

     * @param roundIndex 环的索引 从1开始 1,2,3,4......;

     */

    private createHexagonCircle(roundIndex:number = 1){

        let centerX:number = Laya.stage.width/2;

        let centerY:number = Laya.stage.height/2;

        let r:number = (roundIndex - 1) * 64;

        let cos30:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(30)));

        let _rightBottomX:number = (r * cos30 > 0) ? Math.floor(r * cos30) : Math.ceil(r * cos30);

        let rigthBottomX:number = centerX + _rightBottomX;

        let rightBottomY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(30)));

        let cos90:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(90)));

        let _bottomX:number = (r * cos90 > 0) ? Math.floor(r * cos90) : Math.ceil(r * cos90);

        let bottomX:number = centerX + _bottomX;

        let bottomY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(90)));

        let cos150:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(150)));

        let _leftBottomX:number = (r * cos150 > 0) ? Math.floor(r * cos150) : Math.ceil(r * cos150);

        let leftBottomX:number = centerX + _leftBottomX;

        let leftBottomY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(150)));

        let cos210:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(210)));

        let _leftTopX:number = (r * cos210 > 0) ? Math.floor(r * cos210) : Math.ceil(r * cos210);

        let leftTopX:number = centerX + _leftTopX;

        let leftTopY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(210)));

        let cos270:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(270)));

        let _topX:number = (r * cos270 > 0) ? Math.floor(r * cos270) : Math.ceil(r * cos270);

        let topX:number = centerX + _topX;//Math.floor(r * Math.cos(Laya.Utils.toRadian(270)));

        let topY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(270)));

        let cos330:number = MathUtils.to2Fixed(Math.cos(Laya.Utils.toRadian(330)));

        let _rightTopX:number = (r * cos330 > 0) ? Math.floor(r * cos330) : Math.ceil(r * cos330);

        let rightTopX:number = centerX + _rightTopX;

        let rightTopY:number = centerY + Math.round(r * Math.sin(Laya.Utils.toRadian(330)));

        //每增加一个环小六边形个数增加6

        let hexItemCount:number = (roundIndex == 1) ? 1 : (roundIndex - 1) * 6;

        //相邻两个小六边形的间隔角度(上面分析中的角度差)

        let hexItemAngle:number = 360/hexItemCount;

        let initAngle:number = (roundIndex%2 == 0) ? hexItemAngle : 0;

        let rCount:number = 0;

        let rbCount:number = 0;

        let lbCount:number = 0;

        let lCount:number = 0;

        let ltCount:number = 0;

        let rtCount:number = 0;

        for(let i:number = 0;i<hexItemCount;i++){

            let itemX:number = 0;

            let itemY:number = 0;

            let tempAngle:number = initAngle + hexItemAngle * i;

            //生成 右下角 下 左下角 左上角 上 右上角 节点

            if(tempAngle%60==0){

                r = (roundIndex - 1) * Constants.HexItemHeight;

                let radian:number = Laya.Utils.toRadian(tempAngle-Constants.HexItemOffsetAngle)//30   -Math.PI/6  每个节点偏移30度 具体可以根据需要调节;

                let fixed2Radian:number = MathUtils.to2Fixed(Math.cos(radian));

                let offsetX:number = r * fixed2Radian;

                let realOffsetX:number = (offsetX >= 0) ? Math.floor(offsetX) : Math.ceil(offsetX);

                itemX = centerX + realOffsetX;

                itemY = centerY + Math.round(r * Math.sin(radian));

            }else{

                //生成右边区域节点

                if(tempAngle < Constants.RightBottomAngle){

                    rCount++;

                    itemX = rightTopX;

                    itemY = rightTopY+rCount * Constants.HexItemHeight;

                }else if(tempAngle < Constants.BottomAngle){//生成右下角区域节点

                    rbCount++;

                    itemX = rigthBottomX-rbCount * Constants.HexItemGap;

                    itemY = rightBottomY+rbCount * Constants.HalfHexItemHeight;

                }else if(tempAngle < Constants.LeftBottomAngle){//生成左下角区域节点

                    lbCount++;

                    itemX = bottomX-lbCount * Constants.HexItemGap;

                    itemY = bottomY-lbCount * Constants.HalfHexItemHeight;

                }else if(tempAngle < Constants.LeftAngle){//生成左边区域节点

                    lCount++;

                    itemX = leftBottomX;

                    itemY = leftBottomY-lCount * Constants.HexItemHeight;

                }else if(tempAngle < Constants.LeftTopAngle){//生成左上角区域节点

                    ltCount++;

                    itemX = leftTopX+ltCount * Constants.HexItemGap;

                    itemY = leftTopY-ltCount * Constants.HalfHexItemHeight;

                }else if(tempAngle < Constants.RightTopAngle){//生成右上角区域节点

                    rtCount++;

                    itemX = topX+rtCount * Constants.HexItemGap;

                    itemY = topY+rtCount * Constants.HalfHexItemHeight;

                }

            }

                   this.createHexItem(itemX,itemY,i+1);

        }

    }

private createHexItem(posX:number,posY:number,index:number = 0):Laya.Image{

        //HexItem是一个预制体

        let hexItem:Laya.Image = this.HexItem.create();

        let aa:Laya.Text = hexItem.getChildByName("value") as Laya.Text;

        aa.text = index+"";

        this.owner.addChild(hexItem);

        hexItem.pos(posX,posY);

        return hexItem;

    }

测试一下:

创建一个环:this.createHexagonCircle(5),5是环的索引,从里向外为1,2,3............,如下图:

 创建一个环面盘:this.createHexagonDish(7),7为生成的环数如下图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西溪漫步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值