Jump to content

Voronoi


pascal
 Share

Recommended Posts

Hello.

This plugin can be used to generate a Voronoi texture. It comes with color options, different render options and some other settings that I considered to be useful.

 

 voronoi.zip

The plugin can be found in Effects > Render > Voronoi

 

Preview

demo1.jpg.a8c6b72593688d4e95cb8f95fe7b00c8.jpgdemo2.jpg.10aebd1c0481ba2488305ab03eb5108e.jpgdemo3.jpg.6fc26e28567d37a6b9803006b27da22a.jpgdemo4.jpg.2bb5eee2f03c25cdf1ab67cbbaf964fa.jpgdemo5.jpg.32a782558262bd2a9f7ad9c47319242c.jpg

 

Code

Spoiler
// Name: Voronoi
// Submenu: Render
// Author: pascal
// Title: Render Voronoi
// Version: 1.0
// Desc: Generate a Voronoi texture
// Keywords:
// URL:
// Help:
#region UICode
ColorWheelControl col1 = ColorBgra.FromBgr(0,0,0); // Cell Color
ColorWheelControl col2 = ColorBgra.FromBgr(255,255,255); // Line Color
ListBoxControl mode = 0; // Mode|Cells|Lines|Crystals|Solid
IntSliderControl amount = 15; //[1,100] Count
IntSliderControl size = 200; //[1,1000] Size
IntSliderControl detail = 0; //[0,5] Detail
IntSliderControl bright = 0; //[-100,100] Bias
IntSliderControl contr = 0; //[-100,100] Contrast
IntSliderControl seed = 0; //[0,1000] Seed
#endregion

//global variables
tile[,] tiles;
Rectangle bnds;
int tilesize;
int cols, rows;


void PreRender(Surface dst, Surface src)
{
    bnds = EnvironmentParameters.SelectionBounds;

    //divide canvas into tiles
    tilesize = (int)Math.Ceiling(bnds.Width / (float)amount);
    cols = amount + 4;
    rows = (int)Math.Ceiling(bnds.Height * amount / (float)bnds.Width) + 4;
    tiles = new tile[cols, rows];

    //generate random point for each tile and store tile
    for(int i = 0; i < cols; i++)
    {
        for(int j = 0; j < rows; j++)
        {
            Random random = new Random(seed++*(j+1)*(i+1));
            int tleft = (i-2)*tilesize;
            int tright = (i-1)*tilesize;
            int ttop = (j-2)*tilesize;
            int tbottom = (j-1)*tilesize;
            int randx = random.Next(tleft,tright);
            int randy = random.Next(ttop,tbottom);

            rect tilebounds = new rect(tleft,tright,ttop,tbottom);
            tile t = new tile(tilebounds,new point(randx, randy));
            tiles[i,j] = t;
        }
    }
}


void Render(Surface dst, Surface src, Rectangle rect)
{
    for(int y = rect.Top; y < rect.Bottom; y++)
    {
        for(int x = rect.Left; x < rect.Right; x++)
        {

            //get the corresponding tile index
            int ix = clamp(x / tilesize + 2, 2, cols - 4);
            int iy = clamp(y / tilesize + 2, 2, rows - 4);
            var points = new point[25];

            //get the 25 nearest points based on the surrounding tiles
            for(int i = -2; i <= 2; i++)
            {
                for(int j = -2; j <= 2; j++)
                {
                    tile currentTile = tiles[ix + i, iy + j];
                    points[(i + 2) + 5 * (j + 2)] = currentTile.point;
                }
            }

            //calculate the distance to each point
            int[] dist = new int[points.Count()];
            for(int i = 0; i < dist.Count(); i++)
            {
                int diffX = points[i].x - x;
                int diffY = points[i].y - y;
                dist[i] = diffX*diffX + diffY*diffY;
            }
            //sort the distances and store each index in idx[]
            int[] idx = Enumerable.Range(0, dist.Count()).ToArray();
            Array.Sort<int>(idx, (a, b) => dist[a].CompareTo(dist[b]));

            double col = 0;
//LINE MODE
            if(mode == 1)
            {
                //get 3 nearest points
                var p1 = points[idx[detail]];
                var p2 = points[idx[detail+1]];
                var p3 = points[idx[detail+2]];
                
                //main line
                double distanceSquared = distanceToMidLine(x, y, p1, p2);
                if(distanceSquared <= size/50.0)
                {
                    //smooth line edges
                    col = Math.Max(col, map((float)distanceSquared, size/50f, 0, 0, 1));
                }
                
                //fill in gaps in some lines
                distanceSquared = distanceToMidLine(x, y, p1, p3);
                if(distanceSquared <= size/50.0)
                {
                    col = Math.Max(col, map((float)distanceSquared, size/50f, 0, 0, 1));
                }

                //round line ends
                var e = midPoint(p1, p2, p3);
                double distToEndSquared = (e.x - x) * (e.x - x) + (e.y - y) * (e.y - y);
                if(distToEndSquared <= size/50.0)
                {
                    //draw circle at line edge
                    col = Math.Max(col, map(distToEndSquared, size/50.0, 0, 0, 1));
                } 
            }
//CRYSTAL MODE
            else if(mode == 2)
            {
                //closest 2 points
                var p1 = points[idx[detail]];
                var p2 = points[idx[detail+1]];
                int dist1 = (p1.x - x) * (p1.x - x) + (p1.y - y) * (p1.y - y);
                int dist2 = (p2.x - x) * (p2.x - x) + (p2.y - y) * (p2.y - y);
                //difference in distance
                int diff = dist2 - dist1;
                col = 1f - Math.Max(0, Math.Min(1, diff / 25f / size));
            }
//SOLID MODE
            else if (mode == 3)
            {
                //get closest point
                var p1 = points[idx[detail]];
                //random value with closest point as seed
                Random random = new Random(p1.x * p1.y);
                float diff = (float)random.NextDouble();
                col = Math.Max(0, Math.Min(1, diff));
            }
//CELLS MODE
            else
            {
                //get closest point
                var p = points[idx[detail]];
                //calculated distance squared
                int distX = p.x - x;
                int distY = p.y - y;
                int diff = distX*distX + distY*distY;
                //value based on distance
                col = Math.Max(0, Math.Min(1, diff / 25f / size));
            }

            //apply bias (brightness) and contrast
            col = brightness(col, bright/100f);
            col = contrast(col, contr/50f);
            //calculate color values
            byte R = limit(col*col2.R + (1f-col)*col1.R);
            byte G = limit(col*col2.G + (1f-col)*col1.G);
            byte B = limit(col*col2.B + (1f-col)*col1.B);
            dst[x, y] = ColorBgra.FromBgr(B, G, R);
        }
    }
}


byte limit(double val)
{
    //limit byte between 0 and 255
    return Math.Max((byte)0, Math.Min((byte)255, (byte)val));
}


int clamp(int val, int min, int max)
{
    //clamp value between min and max
    return Math.Max(min, Math.Min(val, max));
}


double map(double val, double min0, double max0, double min1, double max1)
{
    //map value to a new domain
    return min1 + (val - min0) / (max0 - min0) * (max1 - min1);
}


double brightness(double val, double bri)
{
    //apply a brightness effect to the value
    if(bri == 0f) return val;
    if(bri > 0)
    {
        return 1 - Math.Pow(1 - val, 1 + bri);
    }
    else
    {
        return Math.Pow(val, 1 - bri);
    }
}


double contrast(double val, double con)
{
    //apply a contrast effect to the value
    if(con == 0f) return val;
    if(con > 0)
    {
        if(val < 0.5)
        {
            return Math.Pow(2 * val, 1 + con) / 2.0;
        }
        else
        {
            return 1 - Math.Pow(-2 * val + 2, 1 + con) / 2.0;
        }
    }
    else
    {
        if(val < 0.5)
        {
            return 0.5 - Math.Pow(-2 * val + 1, 1 - con) / 2.0;
        }
        else
        {
            return 0.5 + Math.Pow(2 * val - 1, 1 - con) / 2.0;
        }
    }
}


point midPoint(point p1, point p2, point p3)
{
    //matrix calculations to find the midpoint of a circle through 3 points
    int m1 = (p1.x * p1.x + p1.y * p1.y);
    int m2 = (p2.x * p2.x + p2.y * p2.y);
    int m3 = (p3.x * p3.x + p3.y * p3.y);
    int xm1 =
        m1 * p2.y + m2 * p3.y + m3 * p1.y -
        m1 * p3.y - m2 * p1.y - m3 * p2.y;
    int xm2 =
        p1.x * p2.y + p2.x * p3.y + p3.x * p1.y -
        p1.x * p3.y - p2.x * p1.y - p3.x * p2.y;
    int x = (int)(xm1 / (float)xm2 / 2f);
    int ym1 =
        m1 * p2.x + m2 * p3.x + m3 * p1.x -
        m1 * p3.x - m2 * p1.x - m3 * p2.x;
    int y = (int)(ym1 / (float)xm2 / -2f);
    return new point(x, y);
}


double distanceToMidLine(int x, int y, point p1, point p2)
{
    //calculate middle point
    double mx = (p1.x + p2.x) / 2.0;
    double my = (p1.y + p2.y) / 2.0;
    if(p1.x == p2.x) return (y - my) * (y - my);
    if(p1.y == p2.y) return (x - mx) * (x - mx);
    //generate line
    double a = (p2.x - p1.x) / (double)(p2.y - p1.y);
    double b = my + a * mx;
    //calculate distance form pixel to line
    double top = (a * x + y - b);
    return top * top / (a * a + 1);
}


class rect
{
    public int left, right, top, bottom;

    public rect(int l, int r, int t, int b)
    {
        left = l;
        right = r;
        top = t;
        bottom = b;
    }
}


class point
{
    public int x, y;

    public point(int a, int b)
    {
        x = a;
        y = b;
    }
}


class tile
{
    public rect bounds;
    public point point;

    public tile(rect b, point p)
    {
        bounds = b;
        point = p;
    }
}

 

Edited by pascal
moved code into spoiler
  • Like 2
  • Upvote 2
Link to comment
Share on other sites

  • pascal changed the title to Voronoi

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...