pascal Posted November 13, 2021 Share Posted November 13, 2021 (edited) 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 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 November 14, 2021 by pascal moved code into spoiler 2 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.