算法生成N芒星

      前面两个图像生成算法是:道教的太极八卦图和佛教的卐和卍字图。这一节整个洋气的图像:芒星。但愿我别召唤出什么恐怖的禁忌,尤其今晚还是万圣节之夜。平时看玄幻小说,经常读到有关六芒星,七芒星,九芒星的技法。芒星是由几个完全的等腰三角形(有时是正三角形)和一个正多边形组成的二维图形。等腰三角形的个数与正多边形的边数相等。由五个等腰三角形和正五边形组成的图形叫“五芒星”(俗称:五角星)。由六个等腰三角形和正六边形组成的图形叫“六芒星”……依此类推。芒星在美学、历史和占卜都有着很大的用处。

      芒星在生活中与我们息息相关,我们常说的“五角星”也是芒星的一种。五芒星的五个顶点都代表不同的元素,分别是地、水、火、风及象征人类精神力量的第五元素,而五芒星亦是在魔法中是常被使用的符号,因为其整体一看就像一个人的身体,最顶一点为头部,其余为四肢。在圣经中,人是被创造者,而五芒星也含有被创造之意,这就和四元素及人体相对应。当顶点指向天时,那便代表圣力。 

      Tantrism派认为,六芒星形中尖端向下的三角形是卡利·玛的象征物“女阴”的符号,除了倒三角形之外,它也表示为鱼、两端尖锐的椭圆形、马蹄、蛋等图形。因此这个倒三角被称为“女阴的图象”即“Yoni Yantra”,其中“Yantra”是Tantrism派所认为的“冥想的图象”,是适用于眼睛的图象,和“适用于耳的图象”-“真言”(Mantra-曼荼罗,象征为八叶莲花)互为表里。倒三角形代表了万物之源-宇宙之母,表示盛满宇宙之母体液(力量)的容器。

      在西方,7被认为是一个很有魔力的数字。被神秘学视为意义上更复杂的芒星,力量也更强大的图案。关于七芒星的资料: 1 .七芒星很难被准确地画出,因为七芒星是“不平均却稳定的一体” 2.七芒星分为“正七芒星”与“逆七芒星” 3.关于“七芒星魔法阵”也是众说纷纭 4.七芒星魔法阵的功效被记载得不多,只知道大概可以用来召唤超能。因此没有任何魔法师或者术士敢使用,七芒星因此成了禁忌。传说一笔画出过完美七芒星的人有的说看见了炽天使长米迦勒,有的说看见了地狱魔君路西法,也有人说看见了天堂的生命树,甚至有人说看见了末日。

      八芒星与卐字符号是遍布于我国广大地域多个民族的文化符号。有学者认为神秘的卐字符号其实是八角星纹的简化变体,它们都代表了太阳在一个回归年的视循环运动,即一年四季的循环变化。 八角星让人想起罗盘、六分仪、舵,像是朝向八方的宝剑。八角星的八个角代表八个方向,意为绝对的混沌或混沌能量的八种形态(天地风雷水火山泽)。

这篇文章中提供了两套生成芒星的算法,先看第一个:

void            CPixelNStar::ResetN()
{
    m_n = (unsigned int)m_params[0];
    if (m_n < 5)
    {
        m_n = 5;
    }
    else if (m_n > 32)
    {
        m_n = 32;
    }
    
    float radius = 400.0f;

    for (unsigned int i = 0; i < m_n; i++)
    {
        m_listPoints[i].x = radius*sinf((i - 0.5f)*2*PI/m_n);
        m_listPoints[i].y = radius*cosf((i - 0.5f)*2*PI/m_n);
    }
}

unsigned int    CPixelNStar::CalculatePixel(unsigned int x, unsigned int y)
{
    unsigned int red = 0xffff0000;
    unsigned int yellow = 0xffffc000;
    unsigned int gray = 0xff808080;

    Vec2 P(x - 512.0f, y - 512.0f);

    bool bCenter = true;
    for (unsigned int i = 0; i < m_n; i++)
    {
        Vec2& v = m_listPoints[i];
        Vec2& vL1 = m_listPoints[(i + m_n - 1)%m_n];
        Vec2& vL2 = m_listPoints[(i + m_n - 2)%m_n];
        Vec2& vR1 = m_listPoints[(i + 1)%m_n];
        Vec2& vR2 = m_listPoints[(i + 2)%m_n];

        if (!IsPointInAngle(vL2, v, vR2, P))
        {
            bCenter = false;
        }
        else if (IsSameSide(vL1, vR1, v, P))
        {
            return red;
        }
    }
  
    return bCenter ? yellow : gray;
}

关于结构体Vec2的定义及相关函数见:二维平面上判断点在三角形内的最优算法

生成图像有:

五芒星

六芒星

七芒星

八芒星

九芒星

十芒星

十一芒星

十二芒星

十三芒星

二十三芒星

      百度上说:任何芒星都可以一笔画出,并且起笔点和结束点在同一位置。不过这个“一笔”说得太笼统,N芒星需要几条直线才能画出?由图可以看出,N芒星可以由N条线段画出,但只有N为单数时,才可以由N条线段一笔画出。而N为双数时,相当于两个N/2多边形的交错重叠。

 

第一种算法生成的芒星,当N越多时,星尖越短越钝,所以我双写了第二种算法,可以调节星尖的长度:

void            CPixelNLightStar::ResetN()
{
    m_n = (unsigned int)m_params[0];
    if (m_n < 3)
    {
        m_n = 3;
    }
    else if (m_n > 32)
    {
        m_n = 32;
    }
    
    for (unsigned int i = 0; i < m_n; i++)
    {
        m_list_sin[i] = sinf((i - 0.5f)*2*PI/m_n);
        m_list_cos[i] = cosf((i - 0.5f)*2*PI/m_n);
    }
}

unsigned int    CPixelNLightStar::CalculatePixel(unsigned int x, unsigned int y)
{
    float radius = m_params[1];
    float h = radius*m_params[2];

    unsigned int red = 0xffff0000;
    unsigned int yellow = 0xffffc000;
    unsigned int gray = 0xff808080;

    float i = x - 512.0f;
    float j = y - 512.0f;

    float dis = sqrtf(i*i + j*j);
    if (dis < radius*cosf(PI/m_n))
    {
        return red;
    }
    else if (dis > radius + h)
    {
        return gray;
    }

    Vec2 tri0(0.0f, radius + h);
    Vec2 tri1(radius*m_list_sin[0], radius*m_list_cos[0]);
    Vec2 tri2(radius*m_list_sin[1], radius*m_list_cos[1]);
    Vec2 P;

    bool bCenter = true;
    for (unsigned int m = 0; m < m_n; m++)
    {
        P.x = i*m_list_cos[m] - j*m_list_sin[m];
        P.y = i*m_list_sin[m] + j*m_list_cos[m];

        if (IsPointInTriangle(tri0, tri1, tri2, P))
        {
            return yellow;
        }
    }

    if (dis < radius)
    {
        return red;
    }
  
    return gray;
}

算法中可以设置芒星半径长度和角的高度,以生成更多样的芒星:

相应软件:

Why数学图像生成工具

之前我写过一篇与芒星有关的图形画法:

数学图形(1.30) 星星

相关文章:

算法之美---100幅由程序生成的图像,总有一幅让你感到惊艳[上]

算法之美---100幅由程序生成的图像,总有一幅让你感到惊艳[下]

List<Vector3> 弹幕_(float 半径, int 顶点内点数量,float 第一个点角度=0) { Vector3 center = transform.position; List<Vector3> points = new List<Vector3>(); // 计算内圆半径(黄金分割比例) float innerRadius = 半径 * 0.382f; // 五角有5个顶点和5个内点 int totalPoints = 10; // 生成所有关键点(5个外点 + 5个内点) Vector3[] keyPoints = new Vector3[totalPoints]; for (int i = 0; i < totalPoints; i++) { // 计算当前角度(五角需要每72度一个顶点,但交错放置) float angleDeg = 第一个点角度+(i * 36f); // 36度一个点(10个点 * 36 = 360度) // 区分外点和内点(奇数索引为内点,偶数索引为外点) bool isOuterPoint = (i % 2 == 0); float currentRadius = isOuterPoint ? 半径 : innerRadius; // 转换为弧度 float angleRad = angleDeg * Mathf.Deg2Rad; // 计算点位置(在XY平面) Vector3 point = center + new Vector3( Mathf.Cos(angleRad) * currentRadius, Mathf.Sin(angleRad) * currentRadius, 0 ); keyPoints[i] = point; } // 连接点形成五角 for (int i = 0; i < totalPoints; i++) { int nextIndex = (i + 1) % totalPoints; Vector3 startPoint = keyPoints[i]; Vector3 endPoint = keyPoints[nextIndex]; // 添加起点 points.Add(startPoint); // 在两点之间插值生成中间点 for (int j = 1; j < 顶点内点数量; j++) { float t = (float)j / 顶点内点数量; Vector3 midPoint = Vector3.Lerp(startPoint, endPoint, t); points.Add(midPoint); } } return points; } 有以上方法 但是该方法只能输出五角 现在在参数输入多加一个输入参数int N N代表会输出几角 (最低为4角) 给出修改后的方法
08-17
List<Vector3> 弹幕_( float 半径, int 顶点内点数量, float 第一个点角度 = 0, int N = 0, bool 连接非相邻内点 = false, bool 连接相邻内点 = false, float 内半径比例 = 0.5f) { N += 4; // 实际顶点数 = N + 4 Vector3 center = transform.position; List<Vector3> points = new List<Vector3>(); // 计算内圆半径(限制在10%-90%之间) float innerRadius = 半径 * Mathf.Clamp(内半径比例, 0.1f, 0.9f); // 总关键点数 = 2N (N个外点 + N个内点) int totalKeyPoints = 2 * N; // 生成所有关键点 Vector3[] keyPoints = new Vector3[totalKeyPoints]; float angleStep = 360f / totalKeyPoints; for (int i = 0; i < totalKeyPoints; i++) { float angleDeg = 第一个点角度 + (i * angleStep); bool isOuterPoint = (i % 2 == 0); float currentRadius = isOuterPoint ? 半径 : innerRadius; float angleRad = angleDeg * Mathf.Deg2Rad; keyPoints[i] = center + new Vector3( Mathf.Cos(angleRad) * currentRadius, Mathf.Sin(angleRad) * currentRadius, 0 ); } // 连接外点形成形主体 for (int i = 0; i < totalKeyPoints; i++) { int nextIndex = (i + 1) % totalKeyPoints; Vector3 startPoint = keyPoints[i]; Vector3 endPoint = keyPoints[nextIndex]; points.Add(startPoint); for (int j = 1; j < 顶点内点数量; j++) { float t = (float)j / 顶点内点数量; points.Add(Vector3.Lerp(startPoint, endPoint, t)); } } // 收集所有内点(索引为奇数的点) List<Vector3> innerPoints = new List<Vector3>(); for (int i = 1; i < totalKeyPoints; i += 2) { innerPoints.Add(keyPoints[i]); } // 连接相邻内点(形成内多边形) if (连接相邻内点) { for (int i = 0; i < innerPoints.Count; i++) { int nextIndex = (i + 1) % innerPoints.Count; Vector3 startPoint = innerPoints[i]; Vector3 endPoint = innerPoints[nextIndex]; points.Add(startPoint); for (int j = 1; j < 顶点内点数量; j++) { float t = (float)j / 顶点内点数量; points.Add(Vector3.Lerp(startPoint, endPoint, t)); } } } // 连接非相邻内点(形成案)[核心修改] if (连接非相邻内点) { // 计算最优连接步长(根据形类型) int step = CalculateStarStep(N); HashSet<(int, int)> connectedEdges = new HashSet<(int, int)>(); for (int i = 0; i < innerPoints.Count; i++) { int nextIndex = (i + step) % innerPoints.Count; // 创建无向边标识符(避免重复连接) int minIdx = Mathf.Min(i, nextIndex); int maxIdx = Mathf.Max(i, nextIndex); var edgeKey = (minIdx, maxIdx); // 跳过已连接的边 if (connectedEdges.Contains(edgeKey)) continue; connectedEdges.Add(edgeKey); Vector3 startPoint = innerPoints[i]; Vector3 endPoint = innerPoints[nextIndex]; // 添加线段点 points.Add(startPoint); for (int j = 1; j < 顶点内点数量; j++) { float t = (float)j / 顶点内点数量; points.Add(Vector3.Lerp(startPoint, endPoint, t)); } points.Add(endPoint); // 确保线段终点被添加 } } // 闭合形:添加第一个点 points.Add(keyPoints[0]); return points; } // 计算形连接步长(核心算法) private int CalculateStarStep(int vertexCount) { // 四角特殊处理:步长=2形成十架 if (vertexCount == 4) return 2; // 五角及以上:使用黄金分割步长 // 当N为质数时形成单一形,复合数时形成多个形 return (vertexCount % 2 == 0) ? vertexCount / 2 : (int)Mathf.Round(vertexCount * 0.382f); // 黄金分割近似 } 每个内角顶点 会连接到非相邻的内点 比如 当内顶点数量为6 的时候 每个内内点会和三个内点相连 为5时会和两个内点相连,但是目前 你给的这个方法(我这边弄给出的方法) 无论是五角六角形四角形七角 内点都会和中心点相连 修改代码,四角形以上不要和中心点相连
最新发布
08-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值