嗚嗚喔學習筆記

搜尋此網誌

2021年10月7日 星期四

Poisson Disk Sampling - Unity - C#

Poisson Disk Sampling

創造一個自然 而且每個點之間距離均衡的演算法:


演算結果: 長寬 105x105 圓圈半徑:10

Unity C# 版本:


using UnityEngine;

using System.Collections.Generic;

[System.Serializable]

public class PoissonDiscHelper

{

    int _w;

    int _h;

    float _r;

    [SerializeField]

    public List<Vector2> Points = new List<Vector2>();

    [SerializeField]

    List<Vector2> CandidatePoints = new List<Vector2>();


    public List<Vector2> Gen(int w, int h, float r)

    {

        _w = w;

        _h = h;

        _r = r;

        Points.Clear();

        CandidatePoints.Clear();

        AddPointToMax();

        return Points;

    }

    private void AddPointToMax()

    {

        int limit = 9999;

        int idx = 0;

        while ((Points.Count == 0 || CandidatePoints.Count > 0) && idx < limit)

        {

            AddPoint();

            idx++;

        }

    }

    private void AddPoint()

    {

        Vector2 newP;

        if (Points.Count == 0)

        {

            System.Random r = new System.Random();

            newP = new Vector2(r.Next(0, _w), r.Next(0, _h));

        }

        else

        {

            if (CandidatePoints.Count <= 0)

            {

                Debug.LogFormat("Total Done.  Point Count: {0}", Points.Count);

                return;

            }


            int rand = Random.Range(0, CandidatePoints.Count - 1);

            newP = CandidatePoints[rand];

            CandidatePoints.RemoveAt(rand);

        }


        Points.Add(newP);


        int rayCount = 64;

        for (int rayI = 0; rayI < rayCount; rayI++)

        {

            Vector3 dir3 = Rotate(new Vector3(0, 0, 1), Vector3.up, rayI * 360f / rayCount);

            Vector2 dir = new Vector2(dir3.x, dir3.z);

            Vector2 candiate = newP + (dir.normalized * _r);

            if (candiate.x <= _r / 4 || candiate.y <= _r / 4)

            {

                continue;

            }

            if (candiate.x > _w - _r / 4 || candiate.y > _h - _r / 4)

            {

                continue;

            }


            CandidatePoints.Add(candiate);

        }


        foreach (Vector2 p in Points)

        {

            for (int i = CandidatePoints.Count - 1; i >= 0; i--)

            {

                Vector2 cp = CandidatePoints[i];

                if (Vector2.Distance(cp, p) < _r - 0.001f)

                {

                    CandidatePoints.RemoveAt(i);

                }

            }

        }

    }

    private Vector3 Rotate(Vector3 source, Vector3 axis, float angle)

    {

        Quaternion q = Quaternion.AngleAxis(angle, axis);// 旋转系数

        return q * source;// 返回目标点

    }

}



其他可參考演算法:
Best Candidate Sampling
Lloyd's algorithm


參考資料:

https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E9%9B%86%E7%BE%A4%E5%88%86%E6%9E%90-k-means-clustering-e608a7fe1b43


2021年9月26日 星期日

Unity 向量沿著某個軸向旋轉

/// <summary>

/// 计算一个Vector3绕指定轴旋转指定角度后所得到的向量。

/// </summary>

/// <param name="source">旋转前的源Vector3</param>

/// <param name="axis">旋转轴</param>

/// <param name="angle">旋转角度</param>

/// <returns>旋转后得到的新Vector3</returns>

public Vector3 Rotate(Vector3 source, Vector3 axis, float angle)

{

    Quaternion q = Quaternion.AngleAxis(angle, axis);// 旋转系数

    return q * source;// 返回目标点

}

2021年9月1日 星期三

Design pattern - 命令模式

在物件導向程式設計的範疇中,命令模式 是一種設計模式,它嘗試以物件來代表實際行動。命令物件可以把行動(action) 及其參數封裝起來,於是這些行動可以被:
  • 重複多次
  • 取消(如果該物件有實作的話)
  • 取消後又再重做

這裡實現一個可以做 上一步 & 下一步 的命令模式 如果要新增指令 只要繼承 ICommand 並實作 Execute() && Undo() 就行了 使用時只要呼叫 _cmd = new XXXCmd(); _cmd.Execute(); _cmdHistoryMgr.StoreCommand(_cmd);


上一步只需要呼叫 _cmdHistoryMgr.Undo(); 下一步則是呼叫 _cmdHistoryMgr.Redo(); 不需要管Command的細節 達到擴充性。

using System.Collections.Generic;


public interface ICommand

{

void Execute();

void Undo();

}


public interface ICommandManager

{

void StoreCommand(ICommand cmd);

void ClearAllCommand();

void Undo();

void Redo();

}


public class CommandHistoryManager : ICommandManager

{

Stack<ICommand> _undoStack = new Stack<ICommand>();

Stack<ICommand> _redoStack = new Stack<ICommand>();


public void ClearAllCommand()

{

_undoStack.Clear();

_redoStack.Clear();

}

public void StoreCommand(ICommand cmd)

{

_undoStack.Push(cmd);

}


public void Redo()

{

if (_redoStack.Count <= 0)

{

return;

}


ICommand cmd = _redoStack.Pop();

cmd.Execute();


_undoStack.Push(cmd);

}


public void Undo()

{

if(_undoStack.Count <= 0)

{

return;

}


ICommand cmd =_undoStack.Pop();

cmd.Undo();


_redoStack.Push(cmd);

}

}



2021年4月28日 星期三

[C#] Color to uint & uint to Color

1byte => 0~255 byte[3] 剛好可以對應 Color 的RGB( 0~255,0~255,0~255) 如果有要做 儲存 or 讀取 or 傳輸 可以用這轉換減少容量 public static class ColorUtility

{

    static byte[] Byte4 = new byte[4];

    public static Color32 ToColor(uint colorInt)

    {

        byte[] bytes = BitConverter.GetBytes(colorInt);


        byte r = bytes[0];

        byte g = bytes[1];

        byte b = bytes[2];

        byte a = bytes[3];


        return new Color32(r, g, b, a);

    }

    public static uint ToColorInt(Color32 c)

    {

        Byte4[0] = c.r;

        Byte4[1] = c.g;

        Byte4[2] = c.b;

        Byte4[3] = c.a;


        uint colorInt = BitConverter.ToUInt32(Byte4, 0);

        return colorInt;

    }

}


2021年4月22日 星期四

Leet Code 四叉樹建立分享

https://leetcode.com/problems/construct-quad-tree/ 四叉樹維基 四叉樹簡介:



好處在做搜尋時比較快。 O(n) -> O(h) // h為樹的高度 //n 為總數

Leet Code 四叉樹建立分享 簡單來說就是用遞迴去跑 8->4->2->1 以此類推

step 1. 判斷是否全 0 or 全 1 如果是 isLeaf = true, reutrn node

step 2. 如果不是把它分成4等份 呼叫遞迴

step 3. 一直到拆分到只剩 1 為止

/*

// Definition for a QuadTree node.

public class Node {

    public bool val;

    public bool isLeaf;

    public Node topLeft;

    public Node topRight;

    public Node bottomLeft;

    public Node bottomRight;


    public Node() {

        val = false;

        isLeaf = false;

        topLeft = null;

        topRight = null;

        bottomLeft = null;

        bottomRight = null;

    }

    

    public Node(bool _val, bool _isLeaf) {

        val = _val;

        isLeaf = _isLeaf;

        topLeft = null;

        topRight = null;

        bottomLeft = null;

        bottomRight = null;

    }

    

    public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) {

        val = _val;

        isLeaf = _isLeaf;

        topLeft = _topLeft;

        topRight = _topRight;

        bottomLeft = _bottomLeft;

        bottomRight = _bottomRight;

    }

}

*/


public class Solution {

    public Node Construct(int[][] grid)

    {

        if (grid == null)

        {

            return null;

        }


        Node root = new Node();

        root = Link(grid);

        return root;

    }


    public Node Link(int[][] grid)

    {

        Node n = new Node();

        if (grid.Length == 1)

        {

            n.val = grid[0][0] == 1;

            n.isLeaf = true;

            return n;

        }


        int size = grid.Length;

        int halfSize = size / 2;



        bool all1 = true;

        bool all0 = true;

        for (int x = 0; x < size; x++)

        {

            for (int y = 0; y < size; y++)

            {

                all0 = all0 && grid[x][y] == 0;

                all1 = all1 && grid[x][y] == 1;

            }

        }


        if (all1)

        {

            n.val = true;

            n.isLeaf = true;

            return n;

        }


        if (all0)

        {

            n.val = false;

            n.isLeaf = true;

            return n;

        }


        int[][] topleft = new int[halfSize][];

        for (int x = 0; x < halfSize; x++)

        {

            for (int y = 0; y < halfSize; y++)

            {

                if (topleft[y] == null)

                {

                    topleft[y] = new int[halfSize];

                }


                topleft[y][x] = grid[y][x];

            }

        }

        n.topLeft = Link(topleft);


        int[][] topRight = new int[halfSize][];

        for (int x = halfSize; x < halfSize * 2; x++)

        {

            for (int y = 0; y < halfSize; y++)

            {

                if (topRight[y] == null)

                {

                    topRight[y] = new int[halfSize];

                }


                topRight[y][x - halfSize] = grid[y][x];

            }

        }

        n.topRight = Link(topRight);


        int[][] bottomLeft = new int[halfSize][];

        for (int x = 0; x < halfSize; x++)

        {

            for (int y = halfSize; y < halfSize * 2; y++)

            {

                if (bottomLeft[y - halfSize] == null)

                {

                    bottomLeft[y - halfSize] = new int[halfSize];

                }


                bottomLeft[y - halfSize][x] = grid[y][x];

            }

        }

        n.bottomLeft = Link(bottomLeft);


        int[][] bottomRight = new int[halfSize][];

        for (int x = halfSize; x < halfSize * 2; x++)

        {

            for (int y = halfSize; y < halfSize * 2; y++)

            {

                if (bottomRight[y - halfSize] == null)

                {

                    bottomRight[y - halfSize] = new int[halfSize];

                }


                bottomRight[y - halfSize][x - halfSize] = grid[y][x];

            }

        }

        n.bottomRight = Link(bottomRight);


        if (n.topLeft == null && n.topRight == null && n.bottomLeft == null && n.bottomRight == null)

        {

            return null;

        }

        else

        {

            if (n.topLeft.val || n.topRight.val || n.bottomLeft.val || n.bottomRight.val)

            {

                n.val = true;

                return n;

            }

            else

            {

                n.val = false;

                return n;

            }

        }

    }

}