精华内容
下载资源
问答
  • 六边形立体手机图标

    2020-12-24 04:57:11
    该文档为六边形立体手机图标,是一份很不错的参考资料,具有较高参考价值,感兴趣的可以下载看看
  • 这是一套蓝色六边形背景的,微立体风格,工作总结PPT模板,共34张。第一PPT模板网提供精美多边形背景幻灯片模板免费下载; 关键词:六边形、灰色多边形PowerPoint背景图片,蓝色微立体幻灯片图表,通用工作总结PPT...
  • 六边形立体简约总结计划通用ppt模板,本模板共19P,六边形立体创意设计封面,水滴图形微立体目录页、过渡页,商务红灰配色,扁平化图表,适合总结计划、企业宣传、公司介绍、毕业答辩等场景应用的商务总结计划...
  • 这是一套蓝色六边形背景的,微立体风格,工作总结PPT模板,共34张。第一PPT模板网提供精美多边形背景幻灯片模板免费下载; 关键词:六边形、灰色多边形PowerPoint背景图片,蓝色微立体幻灯片图表,通用工作总结PPT...
  • 六边形图片,六边形立体并列关系图表,适合产品说明展示,产品介绍的精美微立体ppt图表。
  • 这是一套蓝色立体六边形背景的,科技感PPT模板,共25张; 幻灯片模板使用了一张蓝色立体蜂窝六边形图片作为背景图。中间使用黄色字体填写科技工作汇报PPT标题文字。界面背景与文字搭配,具有强烈的科技感。 ...
  • 这是一张带有发光效果的,蓝色立体六边形科技PPT背景图,.jpg格式;
  • 这是一套彩色微立体六边形背景的,动态述职报告PPT模板,共45张。第一PPT模板网提供精美微立体风格幻灯片模板免费下载; 关键词:灰色虚拟线条、立体六边形幻灯片背景图片,转正述职报告PPT模板,彩色微立体幻灯片...
  • 这是一张带有发光效果的,蓝色立体六边形科技PPT背景图,.jpg格式;
  • 六边形立体按钮围绕微立体圆排列,精美的并列关系微立体ppt图表,夏影PPT工作室原创图表。
  • 这是一套蓝色六边形背景的,微立体风格,工作总结PPT模板,共34张。第一PPT模板网提供精美多边形背景幻灯片模板免费下载; 关键词:六边形、灰色多边形PowerPoint背景图片,蓝色微立体幻灯片图表,通用工作总结PPT...
  • 六边形年份大字封面创意,梦幻蓝云朵背景,微立体设计,适合企业介绍、公司介绍的彩色六边形创意封面微立体企业介绍ppt模板。
  • 这是一份六边形的3d立体水晶风格幻灯片目录模板,第一PPT模板网,提供免费下载; 关键词:PPT目录,列表,章节,导航,提纲,3d,立体,水晶风格,PowerPoint素材下载,.PPTX格式; 这是一份六边形的3d立体水晶风格...
  • 这是一套彩色微立体六边形背景的,动态述职报告PPT模板,共45张。第一PPT模板网提供精美微立体风格幻灯片模板免费下载; 关键词:灰色虚拟线条、立体六边形幻灯片背景图片,转正述职报告PPT模板,彩色微立体幻灯片...
  • 空间感格子、淡雅灰世界地图背景,六边形创意封面,微立体图表、微立体图文排版等微立体风格设计,适合创业计划、投资合作、公司项目介绍、企业产品宣传等场景应用的微立体创业融资计划书ppt模板。
  • 立体论文答辩ppt模板:课题综述,目前现状,研究目标,研究过程,研究结论,参考文献,彩色微立体风格六边形创意封面,淡雅低三角形背景,微立体风格通用毕业论文答辩ppt模板。
  • 这是一张微立体六边形并列组合关系PPT图表,第一PPT模板网提供幻灯片图表免费下载; PPT图表是微立体PPT图表,PowerPoint图表由四组六边形构成,底部采用了与六边形同色的文本说明文字,用以阐述每个节点的说明。本...
  • 立体个人简历ppt模板,关于我,教育经历,所获荣誉,职业技能,工作经历,作品展示,岗位认知,工作规划,六边形创意封面,时尚微立体求职竞聘个人简历ppt模板。
  • 这是一套彩色微立体六边形背景的,动态述职报告PPT模板,共45张。第一PPT模板网提供精美微立体风格...关键词:灰色虚拟线条、立体六边形幻灯片背景图片,转正述职报告PPT模板,彩色微立体幻灯片图表大全,.PPTX格式;
  • 六边形创意封面微立体求职竞聘个人简历ppt模板.pptx
  • 这是一份六边形的3d立体水晶风格幻灯片目录模板,第一PPT模板网,提供免费下载; 关键词:PPT目录,列表,章节,导航,提纲,3d,立体,水晶风格,PowerPoint素材下载,.PPTX格式;
  • 这是一张微立体六边形并列组合关系PPT图表,第一PPT模板网提供幻灯片图表免费下载; PPT图表是微立体PPT图表,PowerPoint图表由四组六边形构成,底部采用了与六边形同色的文本说明文字,用以阐述每个节点的说明。本...
  • 六边形几何图形创意封面、目录页过渡页,质感淡雅渐变灰背景,微立体风格设计,适合公司介绍,年终汇报、工作总结、个人述职等场景应用的六边形几何图形创意商务蓝微立体ppt模板。
  • 光感半透明六边形创意标题,网点世界地图背景封面,红灰搭配微立体设计,适合商务工作汇报、工作总结报告、创业计划书等应用场景的精美微立体风格ppt模板。
  • 利用CSS3实现一个立体六边形 结果示例图: 示例代码:.test{ font-size:20px; } .container{ width: 500px; height: 420px; position: relative; margin: 0 auto; padding-top: 200px; perspect

    <一>实现一个立体六边形

    结果示例图:


    示例代码:

    .test{
          font-size:20px;
    }
    .container{
         width: 500px;
         height: 420px;
         position: relative;
         margin: 0 auto;
         padding-top: 200px;
         perspective: 3000px;  /*perspective属性定义3D元素距视距的距离,以像素计*/
         -webkit-perspective-origin-y: 330px;/*设置3D元素的基点位置*/
    }
    .carousel{
         width: 100%;
         height: 100%;
         position: absolute;
         transform-style: preserve-3d;/*子元素保留其3D位置*/
         transform: translateZ(-400px) rotateY(0deg);
    }
    .carousel div{
         width:350px;
         height:250px;
         background-color: rgba(138,224,232,0.5);
         display: block;
         position: absolute;
         left: 50%;
         margin-left: -150px;
    }
    .rotate-0{
        transform: rotateY(0deg) translateZ(400px);/* rotate定义旋转,rotateY定义沿Y轴旋转角度;translate定义转换,translateZ定义沿Z轴方向的转换 */
    }
    .rotate-1{
        transform: rotateY(60deg) translateZ(400px);
    }
    .rotate-2{
        transform: rotateY(120deg) translateZ(400px);
    }
    .rotate-3{
        transform: rotateY(180deg) translateZ(400px);
    }
    .rotate-4{
        transform: rotateY(240deg) translateZ(400px);
    }
    .rotate-5{
        transform: rotateY(300deg) translateZ(400px);
    }

    <div id="test">
    	<div class="container">
            <div id="carousel" class="carousel">
                <div class="rotate-0"></div>
                <div class="rotate-1"></div>
                <div class="rotate-2"></div>
                <div class="rotate-3"></div>
                <div class="rotate-4"></div>
                <div class="rotate-5"></div>
            </div>
        </div>
    </div>


    注意:

    利用 CSS3 新特性实现的 3D 元素 在IE上兼容性并不是很好,目前 transform-style:preserve3d 不支持IE,以下备注:

    以下是在http://caniuse.com上搜到的几个属性在不同浏览器兼容性对比图(绿色为完全兼容,浅绿色为兼容一大部分,黄色为兼容一小部分,红色为完全不兼容):

    transform-style:


    transform:


     这就是如何去利用CSS3实现一个立体六边形,我们下一步就是如何让这个立体六边形能够转动起来,实现一个转动的立体六边形 

     链接:利用 CSS3 实现一个转动立体六边形 <二>



    展开全文
  • 实现一个转动的立体六边形 结果示例图: 示例代码: .test{ font-size:20px; } .container{ width: 500px; height: 420px; position: relative; margin: 0 auto; padding-top: 200px; perspective: 2000px...

    < 二 > 实现一个转动的立体六边形

    结果示例图(因为录制工具的问题这个gif显得有些卡顿,在实际网页中并不会):


    示例代码:
    .test{
        font-size:20px;
    }
    .container{
        width: 500px;
        height: 420px;
        position: relative;
        margin: 0 auto;
        padding-top: 200px;
        perspective: 2000px;
        -webkit-perspective: 2000px;
        perspective-origin-y: 100px;
        -webkit-perspective-origin-y: 100px;
    }
    .carousel{
        width: 100%;
        height: 100%;
        position: absolute; 
        transform-style: preserve-3d;
        transform: translateZ(-400px) rotateY(0deg);
        animation:rotate0 10s infinite linear; /* 添加动画名叫rotate0,持续10s,以线性持续运动*/
    }
    .carousel div{
        width:350px;
        height:250px;
        background-color: rgba(72,83,121,0.5);
        display: block;
        position: absolute;
        left: 50%;
        margin-left: -150px;
        font-size:80px;
        color:red;
        display:flex; 
        align-items:center;/*垂直居中*/ 
        justify-content: center;/*水平居中*/
    }
    .rotate-0{
        transform: rotateY(0deg) translateZ(400px);
    }
    .rotate-1{
        transform: rotateY(60deg) translateZ(400px);
    }
    .rotate-2{
        transform: rotateY(120deg) translateZ(400px);
    }
    .rotate-3{
        transform: rotateY(180deg) translateZ(400px);
    }
    .rotate-4{
        transform: rotateY(240deg) translateZ(400px);
    }
    .rotate-5{
        transform: rotateY(300deg) translateZ(400px);
    }
    @keyframes rotate0{ /* 动画rotate0 */
        from{
            transform: translateZ(-400px) rotateY(360deg);
        }to{
            transform: translateZ(-400px) rotateY(0deg);
        }
    }
    

    <div id="test">
    	<div class="container">
            <div id="carousel" class="carousel">
                <div class="rotate-0">1</div>
                <div class="rotate-1">2</div>
                <div class="rotate-2">3</div>
                <div class="rotate-3">4</div>
                <div class="rotate-4">5</div>
                <div class="rotate-5">6</div>
            </div>
        </div>
    </div>

    注:
    animation属性是一个简写属性,它有六个属性,分别是:
    * animation-name:动画名称
    * animation-duration:完成所定义动画曲线持续时间
    * animation-timing-function:动画曲线
    * animation-delay:动画开始之前的延迟时间
    * animation-iteration-count:动画播放次数
    * animation-direction:是否轮流反向播放动画


    animation的兼容性:



    展开全文
  • 六边形 网格

    千次阅读 2017-09-22 10:16:05
    六边形网格在一些游戏中被用到了,但并不像正方形网格那么直截了当的容易使用.我曾经手机了六边形网格相关的资源接近20年了,写这篇向导文章去探索那些最优美的方法,分析成最简洁风格的代码,主要是基于Charles Fu 和...

    原文地址:http://www.redblobgames.com/grids/hexagons/#line-drawing

    六边形网格在一些游戏中被用到了,但并不像正方形网格那么直截了当的容易使用.我曾经手机了六边形网格相关的资源接近20年了,写这篇向导文章去探索那些最优美的方法,分析成最简洁风格的代码,主要是基于Charles Fu 和Clark Verbrugge向导.我将描述制作六边形网格的各种各样的方式,它们之间的关系,就像一些常见的算法一样.该文中许多部分都是互相有关联的;选择一个类型的网格去更新图标,代码,和文本去匹配,感受不同类型网格对应的代码变化,以及他们的原理.

    该文中的样例代码是用伪代码(pseudo-code)写出来的;它们是容易阅读和被理解的,所以你可以参照它们写出自己使用语言对应的实现代码.

    Geometry 几何学

    六边形是六个边的多边形.规则的多边形的所有边都有着相同的长度.我将我们使用的多边形都假定是规则的.典型的六边形网格可以是纵向的 以及横向的.

    纵向的(左)跟横向的(右)如下图:

    六边形有六个边.每个边被两个六边形所共享.六边形有六个角.每个角被3个六边形共享.关于更多的关于中心,边缘,以及角相关的额内容,请看网格部分文章(矩形,六边形,三角形).

    角度

    在一个规则的六边形中,内角都是120度.里面可以容纳六个三角形楔形物,每个等边三角形内角都是60度.角i在坐标系中对应的弧度是(60*i),  size对应的是从中心点到六边形各个角顶点的距离.代码如下:

     
    1. function hex_corner(center, size, i):
    2.    var angle_deg = 60 * i  
    3.    var angle_rad = PI / 180 * angle_deg
    4.    return Point(center.x + size * cos(angle_rad),
    5.                 center.y + size * sin(angle_rad))

    填充一个六边形,通过hex_corner函数进行六个顶点的计算.要画六边形,则使用计算出来的这些顶点进行画线操作即可.

    横向与纵向模式下的区别是x跟y换掉了.横向模式下,角度是:0,60,120,180,240,300

    纵向模式下角度是:30,90,150,210,270,330.

    大小和跨距

    下一步,我们想把几个六边形放在一起研究.在横向模式下,一个六边形的宽度=2*size.两个邻接的六边形在x轴方向上两个邻接六边形中心点间距离=size*3/2.

    六边形的高度=size*sqrt(3),y轴上两个邻接六边形中心间的距离也是等于size*sqrt(3).

    一些游戏中使用像素艺术去生成六边形,它们准确的说是不匹配咱们几何中所说的六边形的.我在这里描述的角度和跨度公式是没有办法为你的六边形进行计算.剩下的篇幅中,将描述在六边形网格中的算法,即使你的六边形是被拉伸或者收缩过的情况下也是可以工作的.

    Coordinate Systems  坐标系统

    现在让我们假定六边形放在网格中.四四方方的网格中,有一种很明显的方式可以去实现它.对于六边形,可以有多种实现方法.我推荐使用立体坐标系统作为主要的表现方式.使用坐标轴或者偏移坐标量来实现地图存储,展示坐标给使用者.

    Offset coordinates 偏移量坐标

    最常见的方法是偏移所有的行或者列.列 命名为col 或者q.  行 命名为row 或者r.你可以偏移奇数或者偶数列或者行,所以每个横向或者纵向的上的六边形都有两个变量来记录位置.

    Cube coordinates立体坐标

    另一种方式可以在三个主坐标轴下布置六边形,不像之前在方形网格中有两个轴那样.这种坐标下有个更优美的对称性.


    我们采用一个立体网格,然后用x+y+x = 0 切割对角线平面.这是一个怪异的想法,但是它确实帮助我们使得六边形网格算法更简单一些.特别是,我们可以重用笛卡尔坐标系统中的标准操作:坐标相加,坐标相减,坐标乘除法缩放操作等.

    注意立体坐标中的三个主轴,观察它们是如何与六个六边形对角线方向上进行通信的;对角线网格轴如何跟六边形网格方向交互的.

    因为我们已经在正方形以及立体网格中实现过的一些算法,立体坐标系统允许我们修改下算法就可以应用上六边形网格了(也就是转换一下坐标).我将打算在本页中的算法中都使用这个坐标系统.如果之前有算法使用其他的坐标系统的话,我将转换坐标们转换为立体坐标,运行那个算法.运行完了,再转换回去.

    学习三维坐标是如何工作在六边形网格中.选择下图中的一个六边形 将高亮三维坐标系统中对应的坐标哟.具体看原文的下图:

    http://www.redblobgames.com/grids/hexagons/

    1.在立体网格中的每个方向都对应六边形网格中的一条线.尝试高亮z值为0,1,2,3的一个六边形,然后观察他们是怎么关联的.行row被标记为蓝色.同样尝试x(绿色)以及y(紫色).

    2.六边形网格中的每个方向都跟立体网格中的两个方向有关联.例如,六边形网格北边在+y和-z之间的区域,所以往北每走一步都会让y+1,z-1.

    立体坐标系统对于六边形问你哥哥坐标系统来说是个挺合理的选择.由于限制x+y+z=0 ,所以算法必须保持那样.这个限制可以确保对于每个六边形都有一个笛卡尔坐标可以对应.

    有许多不同的有效的三维六边形坐标系统.有的会限制为x+y+z=0.我已经在许多系统中试过的唯一一种.你也可以去使用x-y,y-z,z-x去限制三位坐标系统,那样它将有自己独有的有趣的我之前没有探索过的属性集.

    可能你会说,"Amit,但是我想去存储三个数字的坐标.我不知道那样的方式下该怎样去存储一个地图".

    Axial coordinates 轴坐标

    二维轴坐标系统,有时候也可称为梯度坐标,构建方法是从三维立体坐标系统中取出两个来进行组建的.因为我们在立体坐标中的那个限制x+y+z=0 中,第三个坐标是冗余的.二维轴坐标对于地图存储和显示坐标来说是足够有用了.像三维立体坐标系统,你可以用笛卡尔坐标系统中的标准的加,减,乘,除操作.

    有许多的立体坐标系统,以及许多坐标轴系统.我可不打算在文章中去展示所有的关联.我打算挑两种去解释,q=x,r=z.你可以把q想成列,r想成行.

    好处就是这样的坐标系统在偏移网格上算法分析运算都是非常干净的.坏处嘛,存储直角坐标系中的地图的化看起来会有那么一点点怪咯;细节请看地图存储处理这里.有一些算法甚至是比三维立体坐标上都表现的干净,但是对于那些,因为我们曾经限制x+y+z=0 ,所以我们可以计算出第三个隐式坐标并且使用它,只是为了那些必须要用到第三个坐标的算法.

    Axes 轴

    偏移坐标系统是人们最常向导的,因为它跟人们认知中二维方形网格中的笛卡尔坐标系统相似.不幸的是坐标轴的方向却和我们习惯的数学坐标轴方向有些不同,使得问题又有些复杂了.三为立体坐标系统和坐标轴偏移系统沿着轴的方向,就可以得到简单的算法实现,但是对于地图存储则会变得复杂了些.也有其他的坐标系统,也被称为交错的或者是重复的,我还没有探索这块;有的人说它工作起来会比三维立体坐标系统和轴坐标系统更容易.有机会看看去.

    轴是坐标增长的方向.垂直于轴的是一条直线,在它上面都是一个常量.上面展示的那些直线就是存有等值的等高线.上图那些绿线紫线.

    Coordinate conversion  坐标转换

    你可能在你的工程中使用的是轴坐标或者偏移坐标,但是许多算法在三维立体坐标中表示出来才最简单.因此,你需要在各个坐标系统间进行转换.

    轴坐标系统跟立体系统坐标是最接近的,所以转换起来也非常容易:

     
    1. # convert cube to axial
    2. q = x
    3. r = z
    4. # convert axial to cube
    5. x = q
    6. z = r
    7. y = -x-z

    代码中,这两个函数可以写成这样:

     
    1. function cube_to_hex(h): # axial
    2.    var q = h.x
    3.    var r = h.z
    4.    return Hex(q, r)
    5. function hex_to_cube(h): # axial
    6.    var x = h.q
    7.    var z = h.r
    8.    var y = -x-z
    9.    return Cube(x, y, z)
    偏移坐标系统是唯一有些复杂的:
     
    1. # convert cube to even-q offset
    2. col = x
    3. row = z + (x + (x&1)) / 2
    4. # convert even-q offset to cube
    5. x = col
    6. z = row - (col + (col&1)) / 2
    7. y = -x-z
    8. # convert cube to odd-q offset
    9. col = x
    10. row = z + (x - (x&1)) / 2
    11. # convert odd-q offset to cube
    12. x = col
    13. z = row - (col - (col&1)) / 2
    14. y = -x-z
    15. # convert cube to even-r offset
    16. col = x + (z + (z&1)) / 2
    17. row = z
    18. # convert even-r offset to cube
    19. x = col - (row + (row&1)) / 2
    20. z = row
    21. y = -x-z
    22. # convert cube to odd-r offset
    23. col = x + (z - (z&1)) / 2
    24. row = z
    25. # convert odd-r offset to cube
    26. x = col - (row - (row&1)) / 2
    27. z = row
    28. y = -x-z

    实现笔记:我使用a&1而不是a%2来检测是否为奇数或者偶数.更细节的东西请看这里.

    Neighbors   邻居们

    给定一个六边形,哪六个六边形是它的邻居嘞?正如你想的那样,答案跟在三维立体坐标中是一样简单,一直都是跟轴坐标中那样非常简单的,只有偏移坐标系统中会有些复杂.你可能还想去计算6个对角线方向上对应的六边形(后面看到图就知道什么意思了,说不清楚).

    三维立体坐标轴

    在六边形坐标系统中移动,会导致三个坐标中的一个+1,一个-1(总和一定保持为0).有三种+1可能发生的场景,剩下的两个有一个可能会-1.总共6中可能的改变.每种都对应与六边形一个方向上.最简单最快的方法是预先计算出转换的这个表如下图,用的时候直接从这个表中取就好了:

     
    1. var directions = [
    2.   Cube(+1, -1,  0), Cube(+1,  0, -1), Cube( 0, +1, -1),
    3.   Cube(-1, +1,  0), Cube(-1,  0, +1), Cube( 0, -1, +1)
    4. ]
    5. function cube_direction(direction):
    6.    return directions[direction]
    7. function cube_neighbor(hex, direction):
    8.    return cube_add(hex, cube_direction(direction))

    Axial coordinates 轴坐标系统

    正如之前的,我们将使用立体坐标系统作为起始点.根据这个表将(dx,dy,dz)转换为(dq,dr).

     
    1. var directions = [
    2.   Hex(+1,  0), Hex(+1, -1), Hex( 0, -1),
    3.   Hex(-1,  0), Hex(-1, +1), Hex( 0, +1)
    4. ]
    5. function hex_direction(direction):
    6.    return directions[direction]
    7. function hex_neighbor(hex, direction):
    8.    var dir = hex_direction(direction)
    9.    return Hex(hex.q + dir.q, hex.r + dir.r)

    Offset coordinates  偏移坐标

    在偏移坐标系统中,这些改变决定于表格中我们已经给定的信息.如果当前正在偏移行或者列上时,规则是不同的,而不像在那些非偏移行列坐标系统中那样.

    正如上面所说的,我们将构建一个数字表去与给定的列号行号相加处理.然而这一次我们这里将有两个数组,一个是对于奇数列/行 使用的,一个是给偶数行/列用的.观察图表中(1,1)在你选择图中六个方向中的一个时它的变化情况.然后看(2,2).这个表格以及对应的代码对于四种表格类型是各不相同的,挑选一种类型观察一下吧.

     
    1. var directions = [
    2.   [ Hex(+1,  0), Hex( 0, -1), Hex(-1, -1),
    3.     Hex(-1,  0), Hex(-1, +1), Hex( 0, +1) ],
    4.   [ Hex(+1,  0), Hex(+1, -1), Hex( 0, -1),
    5.     Hex(-1,  0), Hex( 0, +1), Hex(+1, +1) ]
    6. ]
    7. function offset_neighbor(hex, direction):
    8.    var parity = hex.row & 1
    9.    var dir = directions[parity][direction]
    10.    return Hex(hex.col + dir.col, hex.row + dir.row)

    用上面查找表是计算邻居最简单的方式了.对于那些你感觉奇怪的数字,可以在这里了解下.

    Diagonals 对角线上的

    在六边形坐标系统中,移动到一个对角线指定的位置上的六边形上时,三个坐标中有一个要或+2或-2,其他的两个则都-1或+1.

     
    1. var diagonals = [
    2.   Cube(+2, -1, -1), Cube(+1, +1, -2), Cube(-1, +2, -1),
    3.   Cube(-2, +1, +1), Cube(-1, -1, +2), Cube(+1, -2, +1)
    4. ]
    5. function cube_diagonal_neighbor(hex, direction):
    6.    return cube_add(hex, diagonals[direction])

    跟之前的一样,你可以消去坐标中的某一维实现向轴坐标的转换,或者通过与计算出来的矩阵表转换为偏移坐标系统坐标.

    Distances  距离

    Cube coordinates 立体坐标系统

    在立体坐标系统中,每一个六边形都是三维的立方体. 在六边形图标中距离为1,而在立体坐标系中距离为2.这使得求距离是简单的.在平面坐标系中,曼哈顿距离=abs(dx)+abs(dy).在立方体坐标系中,曼哈顿距离=abs(dx)+abs(dy)+abs(dz).在六面体表格中距离是下面函数计算结果的一半:

     
    1. function cube_distance(a, b):
    2.    return (abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)) / 2

    任何一个坐标中的三个维度中,其中一个必须是另外两个相加之和,然后票选出那个最大的和就是那个距离了.你可能喜欢分成两部分中的一份或者喜欢称为最大的那个(等于另外两个之和)的维度值作为距离,但是不论怎样,得出的结果都是相同的.

     
    1. function cube_distance(a, b):
    2.    return max(abs(a.x - b.x), abs(a.y - b.y), abs(a.z - b.z))

    在图标中,最大值是被高亮出来了.也请注意每个颜色区域都指示除了6个对角方向中的一个方向.

    Axial coordinates 轴坐标系统

    在轴坐标系统中,第三个维度是被隐藏起来了.可以转换轴坐标为立体坐标然后进行距离计算:

     
    1. function hex_distance(a, b):
    2.    var ac = hex_to_cube(a)
    3.    var bc = hex_to_cube(b)
    4.    return cube_distance(ac, bc)

    如果你编译了内联函数hex_to_cube 和 cube_distance 的话,它将生成下面这段代码:

     
    1. function hex_distance(a, b):
    2.    return (abs(a.q - b.q)
    3.          + abs(a.q + a.r - b.q - b.r)
    4.          + abs(a.r - b.r)) / 2

    有大量的不同方法去计算六边形在轴坐标系统中的距离,但是不论你选用哪种,轴坐标中六边形距离计算都是源于立方体坐标系统中曼哈顿距离的计算方式.例如,

    Offset coordinates  偏移坐标系统

    同轴坐标系统中一样,我们将转换偏移坐标系统中的坐标为立体坐标系统中的立体坐标,然后使用立体坐标计算距离.

     
    1. function offset_distance(a, b):
    2.    var ac = offset_to_cube(a)
    3.    var bc = offset_to_cube(b)
    4.    return cube_distance(ac, bc)

    我们将使用相同的模型到许多用到的算法中:转换六边形网格系统中的坐标为立体坐标系统中的坐标,然后在立体坐标系统中对应的算法,然后把计算出来的结果坐标再转换回去.

    Line drawing  六边形间连线

    我们该怎样去画一条从一个六边形到另一个六边形的线呢?我使用了直线插补法去实现绘画.均匀平滑的画出了N+1个点间的线,并且计算出了当前位置是在哪个六边形中.

    1.首先我们需要计算两个终端节点之间的距离N.

    2.然后平滑下两个终端节点A和B之间的N+1个节点.使用线性插补法,从0到N个节点中,第i个点都对应的坐标都可以通过 A + (B - A) * 1.0/N * i 来计算得出.在图标中这些点用深色以及点描出.计算结果如图所示.

    3.转换每个浮点型样例点坐标为整形的六边形坐标系统中的坐标.这个算法叫做cube_round.

    将这些放在一起就可以画出A到B的线了:

     
    1. function cube_lerp(a, b, t):
    2.    return Cube(a.x + (b.x - a.x) * t,
    3.                a.y + (b.y - a.y) * t,
    4.                a.z + (b.z - a.z) * t)
    5. function cube_linedraw(a, b):
    6.    var N = cube_distance(a, b)
    7.    var results = []
    8.    for each 0 i N:
    9.        results.append(cube_round(cube_lerp(a, b, 1.0/N * i)))
    10.    return results

    更多注意:

    • DDA算法在平面网格集中坐标轴方向上最大的距离就是N.

    • 有时候如果你添加一个很小的值(1e-6,-2e-6级别的增量)到起始点到终点中的某一点的坐标上时,画出来的线将会看起来更加平滑连续。这样将在某个方向上推进线的延伸而且可以避免锐化的边缘出现。
    • cube_lerp函数需要通过浮点坐标返回一个立体坐标。如果你使用的静态类型语言的化,你不可以使用例题类型坐标但是你可以定义FloatCube浮点立体坐标,或者如果你想避免定义其他类型的话,你可以将画线函数中的函数内联。

    • 你可以通过内联cube_lerp函数去优化这段代码,然后再循环外计算B.x-A.x,B.x-A.y,以及1.0/N。乘法操作可以转化为重复的加法运算。你可能在最后发现算法很像DDA算法了。

    • 我使用的是轴坐标系统或者立体坐标系统中画线的,但是如果你想用其他的坐标系统的话,看一下这篇文章

    • 有许多画线的方法。有时候你将想用“super cover”算法。有人给我发送过super cover画线的代码,可惜我还没有学会。

    • Movement Range  移动范围

    Coordinate range 坐标范围

    给定一个中心六边形以及一个距离范围N,哪些六边形是跟中心六边形距离为N的范围之内呢?



    我们可以回顾下六边形距离计算公式,distance = max(abs(dx),abs(dy),abs(dz))。找寻所有的距离在N范围内的六边形,我们需要使用 max(abs(dx),abs(dy),abs(dz)) <= N.意思是 我们需要三个条件都成立abs(dx) <=N  ,abs(dy)<=N ,abs(dz) <=N。移除绝对值符号,我们就得出了 -N<= dx <= N,-N<=dy<=N,-N<=dz<=N .在代码中,它表现为一个嵌套循环:

       
    1. var results = []
    2. for each -N dx N:
    3.    for each -N dy N:
    4.        for each -N dz N:
    5.            if dx + dy + dz = 0:
    6.                results.append(cube_add(center, Cube(dx, dy, dz)))

    虽然这个循环可以计算出结果来,但是却不高效.dz对应的所有值都循环了一遍,但事实上他们中却只有iyige满足立体坐标系统中的限制表达式dx+dy+dz=0 的条件.因此,我们可以直接根据限制表达式直接计算得出dz,而不必再去循环求出.

       
    1. var results = []
    2. for each -N dx N:
    3. for each max(-N, -dx-N) dy min(N, -dx+N):
    4. var dz = -dx-dy
    5. results.append(cube_add(center, Cube(dx, dy, dz)))

    循环迭代了所有坐标中符合条件的坐标点。在图表中,每个范围都对应于一组线。每条线都对应一个不等式。我们挑选出所有满足六个不等式条件的额六边形。

    Intersecting ranges 交叉区域


    如果你需要查看六边形们所在的区域是不是不止一个区域内,你可以在生成六边形之前交叉这些区域看看.

    你可能想用代数方法或者几何方法去解决这些问题.代数方法,每一个区域都应满足不等式-N<= dx <= N所限制的表达式.几何方法解决的话,每一片驱雨都是在3D空间的立体几何,我们打算在3D空间中交叉两个立方体,在3D空间中生成一个新的立方体,然后映射 x+y+z=0 平面中得到六边形.我打算使用代数方法解决这个问题:


    首先,我们重写了限制表达式 -N<= dx <= N为更通用化的形式 Xmin <= x <= Xmax,并且设置Xmin = center.x-N,Xmax=center.x+N.对于y,z 轴做同样的处理,得出之前这个选区问题的更通用化的代码:

      
    1. var results = []
    2. for each xmin x xmax:
    3. for each max(ymin, -x-zmax) y min(ymax, -x-zmin):
    4. var z = -x-y
    5. results.append(Cube(x, y, z))

    数学上两个区间的交叉区域a <= x <= b以及 c <= x <= d,可以合并为max(a,c) <= x <= min(b,d). 因为六边形区域是表示为x,y,z上,我们可以分别计算x,y,z上的交叉区域,然后用嵌套循环去生成在交叉区域中的六边形.对于一个六边形,我们设定Xmin = H.x-N 以及 Xmax = H.x+N ,同样的方法也给y,z轴上设置了.对于两个六边形的交叉区域,我们设置Xmin = max(H1.x - N,H2.x - N)以及Xmax = min(H1.x+N,H2.x+N),同样的方法设置了y,z轴上.这个模型可以在三个或者更多的交叉区域上的工作.

    Obstacles  障碍

     如果存在障碍物的话,限制距离后的泛洪法(广度优先搜索)是最简单不过的事情了.在下图图表中,限制步数为7的情况.在代码中,fringes[k]是一个存储了在限制为k步内可以到达的所有六边形的数组.每一次主循环,都会向外扩张一层.


      
    1. function cube_reachable(start, movement):
    2. var visited = set()
    3. add start to visited
    4. var fringes = []
    5. fringes.append([start])
    6. for each 1 < k movement:
    7. fringes.append([])
    8. for each cube in fringes[k-1]:
    9. for each 0 dir < 6:
    10. var neighbor = cube_neighbor(cube, dir)
    11. if neighbor not in visited, not blocked:
    12. add neighbor to visited
    13. fringes[k].append(neighbor)
    14. return visited

    Rotation 旋转

    不再是考虑一个六边形到另一个六边形了,现在假定给了一个六边形数组(6个刚好组成宏观上的六边形),你可能想要将其旋转一下.


    仔细观察应该可以发现,当前这个宏观六边形中的六个坐标点,右旋转60度后刚好如下面代码中的旋转映射情况:

      
    1. [ x, y, z]
    2. to [-z, -x, -y]

    同理,左旋60度对应的坐标点跟上一个坐标点的映射关系也是有规律的,如下所述:

      
    1. [ x, y, z]
    2. to [-y, -z, -x]

    当你在图表中挪动鼠标时,可以注意到每60度旋转,正负号和坐标位置都翻转了下.120度旋转时坐标上对应的数值对应的正负号又回来了,只是位置还不对;180度旋转时,坐标上对应的数字跟执勤啊的一样了,只是符号变化了.(看图找规律找规律)

    你可以转换这些为轴坐标或者偏移坐标系统.在这里看其他方式的旋转计算.

    Rings

    展开全文
  • 六边形网格

    万次阅读 多人点赞 2015-12-04 13:18:39
    六边形网格在一些游戏中被用到了,但并不像正方形网格那么直截了当的容易使用.我曾经手机了六边形网格相关的资源接近20年了,写这篇向导文章去探索那些最优美的方法,分析成最简洁风格的代码,主要是基于Charles Fu 和...

    原文地址:http://www.redblobgames.com/grids/hexagons/#line-drawing

    六边形网格在一些游戏中被用到了,但并不像正方形网格那么直截了当的容易使用.我曾经手机了六边形网格相关的资源接近20年了,写这篇向导文章去探索那些最优美的方法,分析成最简洁风格的代码,主要是基于Charles Fu 和Clark Verbrugge向导.我将描述制作六边形网格的各种各样的方式,它们之间的关系,就像一些常见的算法一样.该文中许多部分都是互相有关联的;选择一个类型的网格去更新图标,代码,和文本去匹配,感受不同类型网格对应的代码变化,以及他们的原理.

    该文中的样例代码是用伪代码(pseudo-code)写出来的;它们是容易阅读和被理解的,所以你可以参照它们写出自己使用语言对应的实现代码.

    Geometry 几何学

    六边形是六个边的多边形.规则的多边形的所有边都有着相同的长度.我将我们使用的多边形都假定是规则的.典型的六边形网格可以是纵向的 以及横向的.

    纵向的(左)跟横向的(右)如下图:

    六边形有六个边.每个边被两个六边形所共享.六边形有六个角.每个角被3个六边形共享.关于更多的关于中心,边缘,以及角相关的额内容,请看网格部分文章(矩形,六边形,三角形).

    角度

    在一个规则的六边形中,内角都是120度.里面可以容纳六个三角形楔形物,每个等边三角形内角都是60度.角i在坐标系中对应的弧度是(60*i),  size对应的是从中心点到六边形各个角顶点的距离.代码如下:

     
    1. function hex_corner(center, size, i):
    2.    var angle_deg = 60 * i  
    3.    var angle_rad = PI / 180 * angle_deg
    4.    return Point(center.x + size * cos(angle_rad),
    5.                 center.y + size * sin(angle_rad))

    填充一个六边形,通过hex_corner函数进行六个顶点的计算.要画六边形,则使用计算出来的这些顶点进行画线操作即可.

    横向与纵向模式下的区别是x跟y换掉了.横向模式下,角度是:0,60,120,180,240,300

    纵向模式下角度是:30,90,150,210,270,330.

    大小和跨距

    下一步,我们想把几个六边形放在一起研究.在横向模式下,一个六边形的宽度=2*size.两个邻接的六边形在x轴方向上两个邻接六边形中心点间距离=size*3/2.

    六边形的高度=size*sqrt(3),y轴上两个邻接六边形中心间的距离也是等于size*sqrt(3).

    一些游戏中使用像素艺术去生成六边形,它们准确的说是不匹配咱们几何中所说的六边形的.我在这里描述的角度和跨度公式是没有办法为你的六边形进行计算.剩下的篇幅中,将描述在六边形网格中的算法,即使你的六边形是被拉伸或者收缩过的情况下也是可以工作的.

    Coordinate Systems  坐标系统

    现在让我们假定六边形放在网格中.四四方方的网格中,有一种很明显的方式可以去实现它.对于六边形,可以有多种实现方法.我推荐使用立体坐标系统作为主要的表现方式.使用坐标轴或者偏移坐标量来实现地图存储,展示坐标给使用者.

    Offset coordinates 偏移量坐标

    最常见的方法是偏移所有的行或者列.列 命名为col 或者q.  行 命名为row 或者r.你可以偏移奇数或者偶数列或者行,所以每个横向或者纵向的上的六边形都有两个变量来记录位置.

    Cube coordinates立体坐标

    另一种方式可以在三个主坐标轴下布置六边形,不像之前在方形网格中有两个轴那样.这种坐标下有个更优美的对称性.


    我们采用一个立体网格,然后用x+y+x = 0 切割对角线平面.这是一个怪异的想法,但是它确实帮助我们使得六边形网格算法更简单一些.特别是,我们可以重用笛卡尔坐标系统中的标准操作:坐标相加,坐标相减,坐标乘除法缩放操作等.

    注意立体坐标中的三个主轴,观察它们是如何与六个六边形对角线方向上进行通信的;对角线网格轴如何跟六边形网格方向交互的.

    因为我们已经在正方形以及立体网格中实现过的一些算法,立体坐标系统允许我们修改下算法就可以应用上六边形网格了(也就是转换一下坐标).我将打算在本页中的算法中都使用这个坐标系统.如果之前有算法使用其他的坐标系统的话,我将转换坐标们转换为立体坐标,运行那个算法.运行完了,再转换回去.

    学习三维坐标是如何工作在六边形网格中.选择下图中的一个六边形 将高亮三维坐标系统中对应的坐标哟.具体看原文的下图:

    http://www.redblobgames.com/grids/hexagons/

    1.在立体网格中的每个方向都对应六边形网格中的一条线.尝试高亮z值为0,1,2,3的一个六边形,然后观察他们是怎么关联的.行row被标记为蓝色.同样尝试x(绿色)以及y(紫色).

    2.六边形网格中的每个方向都跟立体网格中的两个方向有关联.例如,六边形网格北边在+y和-z之间的区域,所以往北每走一步都会让y+1,z-1.

    立体坐标系统对于六边形问你哥哥坐标系统来说是个挺合理的选择.由于限制x+y+z=0 ,所以算法必须保持那样.这个限制可以确保对于每个六边形都有一个笛卡尔坐标可以对应.

    有许多不同的有效的三维六边形坐标系统.有的会限制为x+y+z=0.我已经在许多系统中试过的唯一一种.你也可以去使用x-y,y-z,z-x去限制三位坐标系统,那样它将有自己独有的有趣的我之前没有探索过的属性集.

    可能你会说,"Amit,但是我想去存储三个数字的坐标.我不知道那样的方式下该怎样去存储一个地图".

    Axial coordinates 轴坐标

    二维轴坐标系统,有时候也可称为梯度坐标,构建方法是从三维立体坐标系统中取出两个来进行组建的.因为我们在立体坐标中的那个限制x+y+z=0 中,第三个坐标是冗余的.二维轴坐标对于地图存储和显示坐标来说是足够有用了.像三维立体坐标系统,你可以用笛卡尔坐标系统中的标准的加,减,乘,除操作.

    有许多的立体坐标系统,以及许多坐标轴系统.我可不打算在文章中去展示所有的关联.我打算挑两种去解释,q=x,r=z.你可以把q想成列,r想成行.

    好处就是这样的坐标系统在偏移网格上算法分析运算都是非常干净的.坏处嘛,存储直角坐标系中的地图的化看起来会有那么一点点怪咯;细节请看地图存储处理这里.有一些算法甚至是比三维立体坐标上都表现的干净,但是对于那些,因为我们曾经限制x+y+z=0 ,所以我们可以计算出第三个隐式坐标并且使用它,只是为了那些必须要用到第三个坐标的算法.

    Axes 轴

    偏移坐标系统是人们最常向导的,因为它跟人们认知中二维方形网格中的笛卡尔坐标系统相似.不幸的是坐标轴的方向却和我们习惯的数学坐标轴方向有些不同,使得问题又有些复杂了.三为立体坐标系统和坐标轴偏移系统沿着轴的方向,就可以得到简单的算法实现,但是对于地图存储则会变得复杂了些.也有其他的坐标系统,也被称为交错的或者是重复的,我还没有探索这块;有的人说它工作起来会比三维立体坐标系统和轴坐标系统更容易.有机会看看去.

    轴是坐标增长的方向.垂直于轴的是一条直线,在它上面都是一个常量.上面展示的那些直线就是存有等值的等高线.上图那些绿线紫线.

    Coordinate conversion  坐标转换

    你可能在你的工程中使用的是轴坐标或者偏移坐标,但是许多算法在三维立体坐标中表示出来才最简单.因此,你需要在各个坐标系统间进行转换.

    轴坐标系统跟立体系统坐标是最接近的,所以转换起来也非常容易:

     
    1. # convert cube to axial
    2. q = x
    3. r = z
    4. # convert axial to cube
    5. x = q
    6. z = r
    7. y = -x-z

    代码中,这两个函数可以写成这样:

     
    1. function cube_to_hex(h): # axial
    2.    var q = h.x
    3.    var r = h.z
    4.    return Hex(q, r)
    5. function hex_to_cube(h): # axial
    6.    var x = h.q
    7.    var z = h.r
    8.    var y = -x-z
    9.    return Cube(x, y, z)
    偏移坐标系统是唯一有些复杂的:
     
    1. # convert cube to even-q offset
    2. col = x
    3. row = z + (x + (x&1)) / 2
    4. # convert even-q offset to cube
    5. x = col
    6. z = row - (col + (col&1)) / 2
    7. y = -x-z
    8. # convert cube to odd-q offset
    9. col = x
    10. row = z + (x - (x&1)) / 2
    11. # convert odd-q offset to cube
    12. x = col
    13. z = row - (col - (col&1)) / 2
    14. y = -x-z
    15. # convert cube to even-r offset
    16. col = x + (z + (z&1)) / 2
    17. row = z
    18. # convert even-r offset to cube
    19. x = col - (row + (row&1)) / 2
    20. z = row
    21. y = -x-z
    22. # convert cube to odd-r offset
    23. col = x + (z - (z&1)) / 2
    24. row = z
    25. # convert odd-r offset to cube
    26. x = col - (row - (row&1)) / 2
    27. z = row
    28. y = -x-z

    实现笔记:我使用a&1而不是a%2来检测是否为奇数或者偶数.更细节的东西请看这里.

    Neighbors   邻居们

    给定一个六边形,哪六个六边形是它的邻居嘞?正如你想的那样,答案跟在三维立体坐标中是一样简单,一直都是跟轴坐标中那样非常简单的,只有偏移坐标系统中会有些复杂.你可能还想去计算6个对角线方向上对应的六边形(后面看到图就知道什么意思了,说不清楚).

    三维立体坐标轴

    在六边形坐标系统中移动,会导致三个坐标中的一个+1,一个-1(总和一定保持为0).有三种+1可能发生的场景,剩下的两个有一个可能会-1.总共6中可能的改变.每种都对应与六边形一个方向上.最简单最快的方法是预先计算出转换的这个表如下图,用的时候直接从这个表中取就好了:

     
    1. var directions = [
    2.   Cube(+1, -1,  0), Cube(+1,  0, -1), Cube( 0, +1, -1),
    3.   Cube(-1, +1,  0), Cube(-1,  0, +1), Cube( 0, -1, +1)
    4. ]
    5. function cube_direction(direction):
    6.    return directions[direction]
    7. function cube_neighbor(hex, direction):
    8.    return cube_add(hex, cube_direction(direction))

    Axial coordinates 轴坐标系统

    正如之前的,我们将使用立体坐标系统作为起始点.根据这个表将(dx,dy,dz)转换为(dq,dr).

     
    1. var directions = [
    2.   Hex(+1,  0), Hex(+1, -1), Hex( 0, -1),
    3.   Hex(-1,  0), Hex(-1, +1), Hex( 0, +1)
    4. ]
    5. function hex_direction(direction):
    6.    return directions[direction]
    7. function hex_neighbor(hex, direction):
    8.    var dir = hex_direction(direction)
    9.    return Hex(hex.q + dir.q, hex.r + dir.r)

    Offset coordinates  偏移坐标

    在偏移坐标系统中,这些改变决定于表格中我们已经给定的信息.如果当前正在偏移行或者列上时,规则是不同的,而不像在那些非偏移行列坐标系统中那样.

    正如上面所说的,我们将构建一个数字表去与给定的列号行号相加处理.然而这一次我们这里将有两个数组,一个是对于奇数列/行 使用的,一个是给偶数行/列用的.观察图表中(1,1)在你选择图中六个方向中的一个时它的变化情况.然后看(2,2).这个表格以及对应的代码对于四种表格类型是各不相同的,挑选一种类型观察一下吧.

     
    1. var directions = [
    2.   [ Hex(+1,  0), Hex( 0, -1), Hex(-1, -1),
    3.     Hex(-1,  0), Hex(-1, +1), Hex( 0, +1) ],
    4.   [ Hex(+1,  0), Hex(+1, -1), Hex( 0, -1),
    5.     Hex(-1,  0), Hex( 0, +1), Hex(+1, +1) ]
    6. ]
    7. function offset_neighbor(hex, direction):
    8.    var parity = hex.row & 1
    9.    var dir = directions[parity][direction]
    10.    return Hex(hex.col + dir.col, hex.row + dir.row)

    用上面查找表是计算邻居最简单的方式了.对于那些你感觉奇怪的数字,可以在这里了解下.

    Diagonals 对角线上的

    在六边形坐标系统中,移动到一个对角线指定的位置上的六边形上时,三个坐标中有一个要或+2或-2,其他的两个则都-1或+1.

     
    1. var diagonals = [
    2.   Cube(+2, -1, -1), Cube(+1, +1, -2), Cube(-1, +2, -1),
    3.   Cube(-2, +1, +1), Cube(-1, -1, +2), Cube(+1, -2, +1)
    4. ]
    5. function cube_diagonal_neighbor(hex, direction):
    6.    return cube_add(hex, diagonals[direction])

    跟之前的一样,你可以消去坐标中的某一维实现向轴坐标的转换,或者通过与计算出来的矩阵表转换为偏移坐标系统坐标.

    Distances  距离

    Cube coordinates 立体坐标系统

    在立体坐标系统中,每一个六边形都是三维的立方体. 在六边形图标中距离为1,而在立体坐标系中距离为2.这使得求距离是简单的.在平面坐标系中,曼哈顿距离=abs(dx)+abs(dy).在立方体坐标系中,曼哈顿距离=abs(dx)+abs(dy)+abs(dz).在六面体表格中距离是下面函数计算结果的一半:

     
    1. function cube_distance(a, b):
    2.    return (abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)) / 2

    任何一个坐标中的三个维度中,其中一个必须是另外两个相加之和,然后票选出那个最大的和就是那个距离了.你可能喜欢分成两部分中的一份或者喜欢称为最大的那个(等于另外两个之和)的维度值作为距离,但是不论怎样,得出的结果都是相同的.

     
    1. function cube_distance(a, b):
    2.    return max(abs(a.x - b.x), abs(a.y - b.y), abs(a.z - b.z))

    在图标中,最大值是被高亮出来了.也请注意每个颜色区域都指示除了6个对角方向中的一个方向.

    Axial coordinates 轴坐标系统

    在轴坐标系统中,第三个维度是被隐藏起来了.可以转换轴坐标为立体坐标然后进行距离计算:

     
    1. function hex_distance(a, b):
    2.    var ac = hex_to_cube(a)
    3.    var bc = hex_to_cube(b)
    4.    return cube_distance(ac, bc)

    如果你编译了内联函数hex_to_cube 和 cube_distance 的话,它将生成下面这段代码:

     
    1. function hex_distance(a, b):
    2.    return (abs(a.q - b.q)
    3.          + abs(a.q + a.r - b.q - b.r)
    4.          + abs(a.r - b.r)) / 2

    有大量的不同方法去计算六边形在轴坐标系统中的距离,但是不论你选用哪种,轴坐标中六边形距离计算都是源于立方体坐标系统中曼哈顿距离的计算方式.例如,

    Offset coordinates  偏移坐标系统

    同轴坐标系统中一样,我们将转换偏移坐标系统中的坐标为立体坐标系统中的立体坐标,然后使用立体坐标计算距离.

     
    1. function offset_distance(a, b):
    2.    var ac = offset_to_cube(a)
    3.    var bc = offset_to_cube(b)
    4.    return cube_distance(ac, bc)

    我们将使用相同的模型到许多用到的算法中:转换六边形网格系统中的坐标为立体坐标系统中的坐标,然后在立体坐标系统中对应的算法,然后把计算出来的结果坐标再转换回去.

    Line drawing  六边形间连线

    我们该怎样去画一条从一个六边形到另一个六边形的线呢?我使用了直线插补法去实现绘画.均匀平滑的画出了N+1个点间的线,并且计算出了当前位置是在哪个六边形中.

    1.首先我们需要计算两个终端节点之间的距离N.

    2.然后平滑下两个终端节点A和B之间的N+1个节点.使用线性插补法,从0到N个节点中,第i个点都对应的坐标都可以通过 A + (B - A) * 1.0/N * i 来计算得出.在图标中这些点用深色以及点描出.计算结果如图所示.

    3.转换每个浮点型样例点坐标为整形的六边形坐标系统中的坐标.这个算法叫做cube_round.

    将这些放在一起就可以画出A到B的线了:

     
    1. function cube_lerp(a, b, t):
    2.    return Cube(a.x + (b.x - a.x) * t,
    3.                a.y + (b.y - a.y) * t,
    4.                a.z + (b.z - a.z) * t)
    5. function cube_linedraw(a, b):
    6.    var N = cube_distance(a, b)
    7.    var results = []
    8.    for each 0 i N:
    9.        results.append(cube_round(cube_lerp(a, b, 1.0/N * i)))
    10.    return results

    更多注意:

    • DDA算法在平面网格集中坐标轴方向上最大的距离就是N.

    • 有时候如果你添加一个很小的值(1e-6,-2e-6级别的增量)到起始点到终点中的某一点的坐标上时,画出来的线将会看起来更加平滑连续。这样将在某个方向上推进线的延伸而且可以避免锐化的边缘出现。
    • cube_lerp函数需要通过浮点坐标返回一个立体坐标。如果你使用的静态类型语言的化,你不可以使用例题类型坐标但是你可以定义FloatCube浮点立体坐标,或者如果你想避免定义其他类型的话,你可以将画线函数中的函数内联。

    • 你可以通过内联cube_lerp函数去优化这段代码,然后再循环外计算B.x-A.x,B.x-A.y,以及1.0/N。乘法操作可以转化为重复的加法运算。你可能在最后发现算法很像DDA算法了。

    • 我使用的是轴坐标系统或者立体坐标系统中画线的,但是如果你想用其他的坐标系统的话,看一下这篇文章

    • 有许多画线的方法。有时候你将想用“super cover”算法。有人给我发送过super cover画线的代码,可惜我还没有学会。

    • Movement Range  移动范围

    Coordinate range 坐标范围

    给定一个中心六边形以及一个距离范围N,哪些六边形是跟中心六边形距离为N的范围之内呢?



    我们可以回顾下六边形距离计算公式,distance = max(abs(dx),abs(dy),abs(dz))。找寻所有的距离在N范围内的六边形,我们需要使用 max(abs(dx),abs(dy),abs(dz)) <= N.意思是 我们需要三个条件都成立abs(dx) <=N  ,abs(dy)<=N ,abs(dz) <=N。移除绝对值符号,我们就得出了 -N<= dx <= N,-N<=dy<=N,-N<=dz<=N .在代码中,它表现为一个嵌套循环:

       
    1. var results = []
    2. for each -N dx N:
    3.    for each -N dy N:
    4.        for each -N dz N:
    5.            if dx + dy + dz = 0:
    6.                results.append(cube_add(center, Cube(dx, dy, dz)))

    虽然这个循环可以计算出结果来,但是却不高效.dz对应的所有值都循环了一遍,但事实上他们中却只有iyige满足立体坐标系统中的限制表达式dx+dy+dz=0 的条件.因此,我们可以直接根据限制表达式直接计算得出dz,而不必再去循环求出.

       
    1. var results = []
    2. for each -N dx N:
    3. for each max(-N, -dx-N) dy min(N, -dx+N):
    4. var dz = -dx-dy
    5. results.append(cube_add(center, Cube(dx, dy, dz)))

    循环迭代了所有坐标中符合条件的坐标点。在图表中,每个范围都对应于一组线。每条线都对应一个不等式。我们挑选出所有满足六个不等式条件的额六边形。

    Intersecting ranges 交叉区域


    如果你需要查看六边形们所在的区域是不是不止一个区域内,你可以在生成六边形之前交叉这些区域看看.

    你可能想用代数方法或者几何方法去解决这些问题.代数方法,每一个区域都应满足不等式-N<= dx <= N所限制的表达式.几何方法解决的话,每一片驱雨都是在3D空间的立体几何,我们打算在3D空间中交叉两个立方体,在3D空间中生成一个新的立方体,然后映射 x+y+z=0 平面中得到六边形.我打算使用代数方法解决这个问题:


    首先,我们重写了限制表达式 -N<= dx <= N为更通用化的形式 Xmin <= x <= Xmax,并且设置Xmin = center.x-N,Xmax=center.x+N.对于y,z 轴做同样的处理,得出之前这个选区问题的更通用化的代码:

      
    1. var results = []
    2. for each xmin x xmax:
    3. for each max(ymin, -x-zmax) y min(ymax, -x-zmin):
    4. var z = -x-y
    5. results.append(Cube(x, y, z))

    数学上两个区间的交叉区域a <= x <= b以及 c <= x <= d,可以合并为max(a,c) <= x <= min(b,d). 因为六边形区域是表示为x,y,z上,我们可以分别计算x,y,z上的交叉区域,然后用嵌套循环去生成在交叉区域中的六边形.对于一个六边形,我们设定Xmin = H.x-N 以及 Xmax = H.x+N ,同样的方法也给y,z轴上设置了.对于两个六边形的交叉区域,我们设置Xmin = max(H1.x - N,H2.x - N)以及Xmax = min(H1.x+N,H2.x+N),同样的方法设置了y,z轴上.这个模型可以在三个或者更多的交叉区域上的工作.

    Obstacles  障碍

     如果存在障碍物的话,限制距离后的泛洪法(广度优先搜索)是最简单不过的事情了.在下图图表中,限制步数为7的情况.在代码中,fringes[k]是一个存储了在限制为k步内可以到达的所有六边形的数组.每一次主循环,都会向外扩张一层.


      
    1. function cube_reachable(start, movement):
    2. var visited = set()
    3. add start to visited
    4. var fringes = []
    5. fringes.append([start])
    6. for each 1 < k movement:
    7. fringes.append([])
    8. for each cube in fringes[k-1]:
    9. for each 0 dir < 6:
    10. var neighbor = cube_neighbor(cube, dir)
    11. if neighbor not in visited, not blocked:
    12. add neighbor to visited
    13. fringes[k].append(neighbor)
    14. return visited

    Rotation 旋转

    不再是考虑一个六边形到另一个六边形了,现在假定给了一个六边形数组(6个刚好组成宏观上的六边形),你可能想要将其旋转一下.


    仔细观察应该可以发现,当前这个宏观六边形中的六个坐标点,右旋转60度后刚好如下面代码中的旋转映射情况:

      
    1. [ x, y, z]
    2. to [-z, -x, -y]

    同理,左旋60度对应的坐标点跟上一个坐标点的映射关系也是有规律的,如下所述:

      
    1. [ x, y, z]
    2. to [-y, -z, -x]

    当你在图表中挪动鼠标时,可以注意到每60度旋转,正负号和坐标位置都翻转了下.120度旋转时坐标上对应的数值对应的正负号又回来了,只是位置还不对;180度旋转时,坐标上对应的数字跟执勤啊的一样了,只是符号变化了.(看图找规律找规律)

    你可以转换这些为轴坐标或者偏移坐标系统.在这里看其他方式的旋转计算.

    Rings



    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 749
精华内容 299
关键字:

六边形立体