public override List <Shape> GetBrushes() { List <Shape> shapes = new List <Shape>(); if (!File.Exists(InputFilePath)) { Console.WriteLine("ERROR: Given file path " + InputFilePath + " does not exist! Not running image generation."); return(shapes); } List <Vector2> edgePositions = new List <Vector2>(); Bitmap map = new Bitmap(InputFilePath); map.RotateFlip(RotateFlipType.Rotate180FlipX); map = new Bitmap(map, new Size(map.Width * 2, map.Height * 2)); int[,] grid = new int[map.Width, map.Height]; int[,] testingGrid = new int[map.Width, map.Height]; for (int x = 0; x < grid.GetLength(0); x++) { for (int y = 0; y < grid.GetLength(1); y++) { grid[x, y] = map.GetPixel(x, y).G > 50 ? 1 : 0; testingGrid[x, y] = grid[x, y]; } } List <List <Point> > emptySpots = new List <List <Point> >(); //Finds all the space between the white space while (true) { Point nextEmptyArea = new Point(); bool foundSpot = false; for (int x = 0; x < testingGrid.GetLength(0); x++) { for (int y = 0; y < testingGrid.GetLength(1); y++) { if (testingGrid[x, y] == 0) { nextEmptyArea = new Point(x, y); foundSpot = true; break; } } if (foundSpot) { break; } } if (foundSpot) { emptySpots.Add(GetEmptySpace(ref testingGrid, nextEmptyArea.X, nextEmptyArea.Y)); } else { break; } } if (emptySpots.Count > 1) { //Gets just the bottom edge of the empty space List <List <Point> > allBottomEdges = new List <List <Point> >(); for (int i = 0; i < emptySpots.Count; i++) { List <Point> bottomEdges = new List <Point>(); for (int j = 0; j < emptySpots[i].Count; j++) { bool edge = GetNeighbor(grid, emptySpots[i][j].X, emptySpots[i][j].Y, 0, 1) == 1; if (edge) { bottomEdges.Add(emptySpots[i][j]); } } allBottomEdges.Add(bottomEdges); } Dictionary <Point, float> hitPointDistances = new Dictionary <Point, float>(); Dictionary <Point, Point> hitPoints = new Dictionary <Point, Point>(); //Further filters the edges to only get the ones that don't collide with themselves (when aiming downward) for (int i = 0; i < allBottomEdges.Count; i++) { for (int j = allBottomEdges[i].Count - 1; j >= 0; j--) { List <Point> tested = new List <Point>(); bool thisHitsSelf = false; Point original = new Point(allBottomEdges[i][j].X, allBottomEdges[i][j].Y); Point curr = original; while (true) { tested.Add(curr); int below = GetNeighbor(grid, curr.X, curr.Y, 0, 1); if (below == -1) { thisHitsSelf = true; break; } Point belowPoint = new Point(curr.X, curr.Y + 1); if (below == 0) { for (int k = 0; k < emptySpots[i].Count; k++) { if (belowPoint == emptySpots[i][k]) { thisHitsSelf = true; break; } } hitPointDistances.Add(original, Vector2.Distance(new Vector2(original.X, original.Y), new Vector2(belowPoint.X, belowPoint.Y))); hitPoints.Add(original, belowPoint); break; } if (thisHitsSelf) { break; } curr = belowPoint; } if (thisHitsSelf) { allBottomEdges[i].RemoveAt(j); } } } List <List <Vector2> > closestOptions = new List <List <Vector2> >(); int amountOfClosestPointsToGet = 100; for (int i = 0; i < allBottomEdges.Count; i++) { List <Vector2> closestPoints = new List <Vector2>(); for (int k = 0; k < amountOfClosestPointsToGet; k++) { float closestDist = 100000; Point closest = new Point(); for (int j = allBottomEdges[i].Count - 1; j >= 0; j--) { float dist = hitPointDistances[allBottomEdges[i][j]]; if (dist < closestDist) { closestDist = dist; closest = allBottomEdges[i][j]; } } closestPoints.Add(new Vector2(closest.X, closest.Y)); allBottomEdges[i].Remove(closest); if (allBottomEdges[i].Count <= 0) { break; } } closestOptions.Add(closestPoints); } Dictionary <int, List <int> > fromConnections = new Dictionary <int, List <int> >(); List <List <Point> > linesToCreate = new List <List <Point> >(); for (int i = 0; i < emptySpots.Count; i++) { fromConnections.Add(i, new List <int>()); } for (int i = 0; i < closestOptions.Count; i++) { List <Point> line = new List <Point>(); int rayLength = 20; List <PixelFlat> flatnessValues = new List <PixelFlat>(); for (int j = 0; j < closestOptions[i].Count; j++) { Console.WriteLine("Calculating best split location, checking point " + (j + 1)); //Top Point evalPointStart = new Point((int)closestOptions[i][j].X, (int)closestOptions[i][j].Y); Point insideRightStart = PixelRaycast(grid, evalPointStart, new Point(1, 0), 1, rayLength); Point insideLeftStart = PixelRaycast(grid, evalPointStart, new Point(-1, 0), 1, rayLength); Point outsideRightStart = PixelRaycast(grid, new Point(evalPointStart.X, evalPointStart.Y + 1), new Point(1, 0), 0, rayLength); Point outsideLeftStart = PixelRaycast(grid, new Point(evalPointStart.X, evalPointStart.Y + 1), new Point(-1, 0), 0, rayLength); Vector2 outsideMiddleStart = Vector2.Lerp(new Vector2(outsideLeftStart.X, outsideLeftStart.Y), new Vector2(outsideRightStart.X, outsideRightStart.Y), 0.5f); float outsideCheckStart = (rayLength * 0.5f) * Vector2.Distance(outsideMiddleStart, new Vector2(evalPointStart.X, evalPointStart.Y)); Vector2 insideMiddleStart = Vector2.Lerp(new Vector2(insideLeftStart.X, insideLeftStart.Y), new Vector2(insideRightStart.X, insideRightStart.Y), 0.5f); float insideCheckStart = (rayLength * 0.5f) * Vector2.Distance(insideMiddleStart, new Vector2(evalPointStart.X, evalPointStart.Y)); float outsideLengthStart = Vector2.Distance(new Vector2(outsideLeftStart.X, outsideLeftStart.Y), new Vector2(outsideRightStart.X, outsideRightStart.Y)); float insideLengthStart = Vector2.Distance(new Vector2(insideLeftStart.X, insideLeftStart.Y), new Vector2(insideRightStart.X, insideRightStart.Y)); float lengthDifferenceStart = (rayLength * 4) - (outsideLengthStart + insideLengthStart); float totalValidAreaStart = outsideCheckStart + insideCheckStart + lengthDifferenceStart; //Bottom Point evalPointEnd = hitPoints[evalPointStart]; Point insideRightEnd = PixelRaycast(grid, new Point(evalPointEnd.X, evalPointEnd.Y + 1), new Point(1, 0), 1, rayLength); Point insideLeftEnd = PixelRaycast(grid, new Point(evalPointEnd.X, evalPointEnd.Y + 1), new Point(-1, 0), 1, rayLength); Point outsideRightEnd = PixelRaycast(grid, new Point(evalPointEnd.X, evalPointEnd.Y - 1), new Point(1, 0), 0, rayLength); Point outsideLeftEnd = PixelRaycast(grid, new Point(evalPointEnd.X, evalPointEnd.Y - 1), new Point(-1, 0), 0, rayLength); Vector2 outsideMiddleEnd = Vector2.Lerp(new Vector2(outsideLeftEnd.X, outsideLeftEnd.Y), new Vector2(outsideRightEnd.X, outsideRightEnd.Y), 0.5f); float outsideCheckEnd = (rayLength * 0.5f) * Vector2.Distance(outsideMiddleEnd, new Vector2(evalPointEnd.X, evalPointEnd.Y)); Vector2 insideMiddleEnd = Vector2.Lerp(new Vector2(insideLeftEnd.X, insideLeftEnd.Y), new Vector2(insideRightEnd.X, insideRightEnd.Y), 0.5f); float insideCheckEnd = (rayLength * 0.5f) * Vector2.Distance(insideMiddleEnd, new Vector2(evalPointEnd.X, evalPointEnd.Y)); float outsideLengthEnd = Vector2.Distance(new Vector2(outsideLeftEnd.X, outsideLeftEnd.Y), new Vector2(outsideRightEnd.X, outsideRightEnd.Y)); float insideLengthEnd = Vector2.Distance(new Vector2(insideLeftEnd.X, insideLeftEnd.Y), new Vector2(insideRightEnd.X, insideRightEnd.Y)); float lengthDifferenceEnd = (rayLength * 4) - (outsideLengthEnd + insideLengthEnd); float totalValidAreaEnd = outsideCheckEnd + insideCheckEnd + lengthDifferenceEnd; flatnessValues.Add(new PixelFlat() { Point = evalPointStart, FlatnessValue = totalValidAreaStart + totalValidAreaEnd }); if (i == -1) { VMFDebug.CreateDebugImage("EvalOption" + j, onDraw: (g) => { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { g.DrawRectangle(grid[i, j] == 0 ? Pens.Gray : Pens.White, new Rectangle(i, j, 1, 1)); } } g.DrawLine(Pens.DarkBlue, evalPointStart, insideRightStart); g.DrawLine(Pens.DarkBlue, evalPointStart, insideLeftStart); g.DrawLine(Pens.LightBlue, new Point(evalPointStart.X, evalPointStart.Y + 1), outsideRightStart); g.DrawLine(Pens.LightBlue, new Point(evalPointStart.X, evalPointStart.Y + 1), outsideLeftStart); g.DrawEllipse(Pens.DarkBlue, new Rectangle((int)insideMiddleStart.X, (int)insideMiddleStart.Y, 2, 2)); g.DrawEllipse(Pens.LightBlue, new Rectangle((int)outsideMiddleStart.X, (int)outsideMiddleStart.Y, 2, 2)); g.DrawLine(Pens.DarkGreen, new Point(evalPointEnd.X, evalPointEnd.Y + 1), insideRightEnd); g.DrawLine(Pens.DarkGreen, new Point(evalPointEnd.X, evalPointEnd.Y + 1), insideLeftEnd); g.DrawLine(Pens.LightGreen, new Point(evalPointEnd.X, evalPointEnd.Y - 1), outsideRightEnd); g.DrawLine(Pens.LightGreen, new Point(evalPointEnd.X, evalPointEnd.Y - 1), outsideLeftEnd); g.DrawEllipse(Pens.DarkGreen, new Rectangle((int)insideMiddleEnd.X, (int)insideMiddleEnd.Y, 2, 2)); g.DrawEllipse(Pens.LightGreen, new Rectangle((int)outsideMiddleEnd.X, (int)outsideMiddleEnd.Y, 2, 2)); g.DrawLine(Pens.Red, evalPointStart, evalPointEnd); g.DrawRectangle(Pens.Black, new Rectangle((int)evalPointStart.X, (int)evalPointStart.Y, 1, 1)); g.DrawRectangle(Pens.Black, new Rectangle((int)evalPointEnd.X, (int)evalPointEnd.Y, 1, 1)); g.DrawString("Total: " + (totalValidAreaStart + totalValidAreaEnd) + "\n" + "Top Middle Check: " + (outsideCheckStart + insideCheckStart) + "\n" + "Top Length Check: " + (lengthDifferenceStart) + "/" + (rayLength * 4) + "\n" + "Bottom Middle Check: " + (outsideCheckEnd + insideCheckEnd) + "\n" + "Bottom Length Check: " + (lengthDifferenceEnd) + "/" + (rayLength * 4), SystemFonts.DefaultFont, Brushes.Black, new PointF(0, 0)); }, map.Width, map.Height); } } flatnessValues.Sort((x, y) => { return(x.FlatnessValue.CompareTo(y.FlatnessValue)); }); //Not sure if theres a better way to choose out of the top contenders, maybe explore later Point current = flatnessValues[0].Point; Point goal = hitPoints[new Point((int)current.X, (int)current.Y)]; int from = -1; int to = -1; for (int j = 0; j < emptySpots.Count; j++) { for (int k = 0; k < emptySpots[j].Count; k++) { if (emptySpots[j][k] == current) { from = j; break; } } if (from != -1) { break; } } for (int j = 0; j < emptySpots.Count; j++) { for (int k = 0; k < emptySpots[j].Count; k++) { if (emptySpots[j][k] == goal) { to = j; break; } } if (to != -1) { break; } } bool loops = false; List <int> visited = new List <int>(); Queue <int> toCheck = new Queue <int>(); toCheck.Enqueue(from); int check = -1; while (toCheck.Count > 0) { check = toCheck.Dequeue(); visited.Add(check); if (check == to) { loops = true; break; } if (fromConnections[check].Count == 0) { continue; } for (int j = 0; j < fromConnections[check].Count; j++) { if (!visited.Contains(fromConnections[check][j])) { toCheck.Enqueue(fromConnections[check][j]); } } } if (!loops) { fromConnections[to].Add(from); while (true) { line.Add(current); if (current == goal) { break; } current = new Point(current.X, current.Y + 1); } linesToCreate.Add(line); } } Console.WriteLine("Adding loop points"); for (int i = 0; i < linesToCreate.Count; i++) { for (int j = 0; j < linesToCreate[i].Count; j++) { grid[linesToCreate[i][j].X, linesToCreate[i][j].Y] = 0; } } VMFDebug.CreateDebugImage("LoopPoints", onDraw: (g) => { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { g.DrawRectangle(grid[i, j] == 0 ? Pens.Gray : Pens.White, new Rectangle(i, j, 1, 1)); } } for (int i = 0; i < emptySpots.Count; i++) { g.DrawString(i.ToString(), SystemFonts.DefaultFont, Brushes.Red, new PointF(emptySpots[i][0].X, emptySpots[i][0].Y)); } for (int i = 0; i < linesToCreate.Count; i++) { Vector2 pos = Vector2.Lerp(new Vector2(linesToCreate[i][0].X, linesToCreate[i][0].Y), new Vector2(linesToCreate[i][linesToCreate[i].Count - 1].X, linesToCreate[i][linesToCreate[i].Count - 1].Y), 0.5f); g.DrawString(i.ToString(), SystemFonts.DefaultFont, Brushes.Blue, new PointF(pos.X, pos.Y)); } for (int i = 0; i < closestOptions.Count; i++) { for (int j = 0; j < closestOptions[i].Count; j++) { g.DrawRectangle(Pens.Black, new Rectangle((int)closestOptions[i][j].X, (int)closestOptions[i][j].Y, 1, 1)); } } }, map.Width, map.Height); } List <Vector2> linedUpPoints = new List <Vector2>(); bool[,] boolGrid = new bool[grid.GetLength(0), grid.GetLength(1)]; for (int x = 0; x < grid.GetLength(0); x++) { for (int y = 0; y < grid.GetLength(1); y++) { boolGrid[x, y] = grid[x, y] == 1; } } linedUpPoints = new List <Vector2>( BitmapHelper.CreateFromBitmap(new ArrayBitmap(boolGrid)) ); List <Vector2> sharedPoints = new List <Vector2>(); Dictionary <int, Vector2> toMove = new Dictionary <int, Vector2>(); List <int> badValues = new List <int>(); bool swapValueToRemove = true; for (int i = linedUpPoints.Count - 1; i >= 0; i--) { for (int j = linedUpPoints.Count - 1; j >= i; j--) { if (i == j) { continue; } float dist = Vector2.Distance(linedUpPoints[i], linedUpPoints[j]); if (dist <= 2 && !sharedPoints.Contains(linedUpPoints[i]) && !sharedPoints.Contains(linedUpPoints[j])) { int diff = j - i; if (diff < 4) { if (swapValueToRemove) { badValues.Add(i); } else { badValues.Add(j); } swapValueToRemove = !swapValueToRemove; } else { sharedPoints.Add(linedUpPoints[i]); sharedPoints.Add(linedUpPoints[j]); toMove.Add(i, linedUpPoints[j]); } } } } foreach (int key in toMove.Keys) { linedUpPoints[key] = toMove[key]; } for (int i = linedUpPoints.Count; i >= 0; i--) { if (badValues.Contains(i)) { linedUpPoints.RemoveAt(i); } } VMFDebug.CreateDebugImage("ImageProcess", onDraw: (g) => { float scale = 0.4f; for (int i = 0; i < linedUpPoints.Count; i++) { int iN = (i + 1) % (linedUpPoints.Count); g.DrawRectangle(new Pen(Color.Black), new Rectangle((int)(linedUpPoints[i].X * scale), (int)(linedUpPoints[i].Y * scale), 1, 1)); g.DrawLine(new Pen(Color.Black, 3), new Point((int)(linedUpPoints[i].X * scale), (int)(linedUpPoints[i].Y * scale)), new Point((int)(linedUpPoints[iN].X * scale), (int)(linedUpPoints[iN].Y * scale))); if (linedUpPoints[i] == linedUpPoints[iN]) { g.DrawEllipse(new Pen(Color.Blue, 3), new Rectangle((int)(linedUpPoints[i].X * scale), (int)(linedUpPoints[i].Y * scale), 1, 1)); } g.DrawString(i.ToString(), SystemFonts.DefaultFont, Brushes.Red, new Point((int)(linedUpPoints[i].X * scale), (int)(linedUpPoints[i].Y * scale))); } for (int i = 0; i < linedUpPoints.Count; i++) { g.DrawRectangle(new Pen(Color.FromArgb(i % 255, i % 255, i % 255)), new Rectangle((int)(linedUpPoints[i].X * scale), (int)(linedUpPoints[i].Y * scale), 1, 1)); } }); Polygon poly = new Polygon() { Visgroup = Visgroups.TAR_LAYOUT, Position = new Vector3(-map.Width, -map.Height, 0), Data = new PolygonShapeData() { Depth = 64, Scalar = 2, PolygonPoints = linedUpPoints } }; shapes.Add(poly); shapes.AddRange(WallGenerator.CreateWalls(poly, new WallData() { Height = 256, Thickness = 16 })); return(shapes); }
/// <summary> /// My triangulation function. I used this https://www.gamedev.net/tutorials/programming/graphics/polygon-triangulation-r3334/ /// as a guide and it was incredibly helpful. I also made my own modifications for my own usecases and attempts at fixes. /// </summary> /// <param name="Polygon"></param> /// <returns></returns> public static List <List <Vector2> > Triangulate(List <Vector2> Polygon) { //These are purely dummy values used for debug drawing int c2 = 0; int c = 0; List <Vector2> sharedValues = Polygon.GroupBy(x => x).Where(g => g.Count() > 1).Select(x => x.Key).ToList(); float amountToRaise = 0.1f; List <int> higherIndices = new List <int>(); Dictionary <Vector2, int> highestIndex = new Dictionary <Vector2, int>(); Dictionary <Vector2, float> currentHighest = new Dictionary <Vector2, float>(); List <Vector2> onesToReset = new List <Vector2>(); for (int i = 0; i < sharedValues.Count; i++) { highestIndex.Add(sharedValues[i], -1); currentHighest.Add(sharedValues[i], 0); } for (int i = 0; i < Polygon.Count; i++) { if (!sharedValues.Contains(Polygon[i])) { continue; } int indexA = (i - 1) % Polygon.Count; if (indexA < 0) { indexA = Polygon.Count - 1; } int indexB = (i) % Polygon.Count; int indexC = (i + 1) % Polygon.Count; Vector2 prev = Polygon[indexA]; Vector2 curr = Polygon[indexB]; Vector2 next = Polygon[indexC]; if ((prev.Y + next.Y) > currentHighest[Polygon[i]]) { currentHighest[Polygon[i]] = (prev.Y + next.Y); highestIndex[Polygon[i]] = i; } } //So this is all my attempt to bypass issues with connected polygons. When I have two edges that are right next to each other, and share points //I detect those, and one set I move *slightly* higher. That way the algorithm will treat them separately and doesnt get confused. //Then when its done all it need to do, I simply move them back down. It works okay enough? Some work arounds were needed but its working. foreach (Vector2 pos in highestIndex.Keys) { Polygon[highestIndex[pos]] = new Vector2(Polygon[highestIndex[pos]].X, Polygon[highestIndex[pos]].Y + amountToRaise); onesToReset.Add(Polygon[highestIndex[pos]]); } List <Vector2> remainingValues = new List <Vector2>(Polygon); List <List <Vector2> > result = new List <List <Vector2> >(); int index = -1; bool triangleMade = true; while (triangleMade) { triangleMade = false; for (int v = Polygon.Count + 2; v >= 0; v--) { index = v; int indexA = (index - 1) % Polygon.Count; if (indexA < 0) { indexA = Polygon.Count - 1; } int indexB = (index) % Polygon.Count; int indexC = (index + 1) % Polygon.Count; Vector2 prev = Polygon[indexA]; Vector2 curr = Polygon[indexB]; Vector2 next = Polygon[indexC]; VMFDebug.CreateDebugImage("TriangulationStepAttempt" + c2, onDraw: (g) => { float scale = 0.2f; Point positionAdjustment = new Point(0, 0); Pen whitePen = new Pen(Color.White, 3); Pen greyPen = new Pen(Color.Gray, 3); Pen blackPen = new Pen(Color.Black, 3); Pen redPen = new Pen(Color.Red, 3); Pen bluePen = new Pen(Color.Blue, 3); Pen greenPen = new Pen(Color.Green, 3); Pen otherPen = new Pen(Color.LightBlue, 3); for (int i = 0; i < Polygon.Count; i++) { int iN = (i + 1) % Polygon.Count; Point p1 = new Point((int)(Polygon[i].X * scale + positionAdjustment.X), (int)(Polygon[i].Y * scale + positionAdjustment.Y)); Point p2 = new Point((int)(Polygon[iN].X * scale + positionAdjustment.X), (int)(Polygon[iN].Y * scale + positionAdjustment.Y)); g.DrawLine(greyPen, p1, p2); } for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result[i].Count; j++) { int iN = (j + 1) % result[i].Count; Point p1 = new Point((int)(result[i][j].X * scale + positionAdjustment.X), (int)(result[i][j].Y * scale + positionAdjustment.Y)); Point p2 = new Point((int)(result[i][iN].X * scale + positionAdjustment.X), (int)(result[i][iN].Y * scale + positionAdjustment.Y)); g.DrawLine(blackPen, p1, p2); } } Point prevP = new Point((int)(prev.X * scale + positionAdjustment.X), (int)(prev.Y * scale + positionAdjustment.Y)); Point currP = new Point((int)(curr.X * scale + positionAdjustment.X), (int)(curr.Y * scale + positionAdjustment.Y)); Point nextP = new Point((int)(next.X * scale + positionAdjustment.X), (int)(next.Y * scale + positionAdjustment.Y)); g.DrawLine(otherPen, prevP, currP); g.DrawLine(otherPen, currP, nextP); g.DrawLine(otherPen, nextP, prevP); g.DrawEllipse(redPen, prev.X * scale + positionAdjustment.X, prev.Y * scale + positionAdjustment.Y, 10, 10); g.DrawEllipse(bluePen, curr.X * scale + positionAdjustment.X, curr.Y * scale + positionAdjustment.Y, 10, 10); g.DrawEllipse(greenPen, next.X * scale + positionAdjustment.X, next.Y * scale + positionAdjustment.Y, 10, 10); }); c2++; //2D cross product or wedge product or whatever you wanna call it Vector2 v1 = next - curr; Vector2 v2 = prev - curr; float val = (v1.X * v2.Y) - (v1.Y * v2.X); if (val <= 0) { continue; } bool noGood = false; for (int i = 0; i < remainingValues.Count; i++) { Vector2 point = remainingValues[i]; Vector2 pointButShort = new Vector2(point.X, MathF.Round(point.Y - amountToRaise, 1)); if (sharedValues.Contains(pointButShort)) { point = pointButShort; } Vector2 currPoint = point; Vector2 altCurrPoint = new Vector2(currPoint.X, MathF.Round(currPoint.Y + amountToRaise, 1)); if (currPoint == prev || currPoint == curr || currPoint == next || altCurrPoint == prev || altCurrPoint == curr || altCurrPoint == next) { continue; } if (PointInTriangle(remainingValues[i], prev, curr, next)) { noGood = true; break; } } if (noGood) { continue; } result.Add(new List <Vector2>() { prev, curr, next }); Polygon.Remove(curr); triangleMade = true; if (Polygon.Count > 2) { VMFDebug.CreateDebugImage("TriangulationStep" + c, onDraw: (g) => { float scale = 0.15f; Point positionAdjustment = new Point(0, 0); Pen whitePen = new Pen(Color.White, 3); Pen greyPen = new Pen(Color.Gray, 3); Pen blackPen = new Pen(Color.Black, 3); Pen redPen = new Pen(Color.Red, 3); Pen bluePen = new Pen(Color.Blue, 3); Pen greenPen = new Pen(Color.Green, 3); for (int i = 0; i < Polygon.Count; i++) { int iN = (i + 1) % Polygon.Count; Point p1 = new Point((int)(Polygon[i].X * scale + positionAdjustment.X), (int)(Polygon[i].Y * scale + positionAdjustment.Y)); Point p2 = new Point((int)(Polygon[iN].X * scale + positionAdjustment.X), (int)(Polygon[iN].Y * scale + positionAdjustment.Y)); g.DrawLine(greyPen, p1, p2); } for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result[i].Count; j++) { int iN = (j + 1) % result[i].Count; Point p1 = new Point((int)(result[i][j].X * scale + positionAdjustment.X), (int)(result[i][j].Y * scale + positionAdjustment.Y)); Point p2 = new Point((int)(result[i][iN].X * scale + positionAdjustment.X), (int)(result[i][iN].Y * scale + positionAdjustment.Y)); g.DrawLine(blackPen, p1, p2); } } g.DrawEllipse(redPen, prev.X * scale + positionAdjustment.X, prev.Y * scale + positionAdjustment.Y, 10, 10); g.DrawEllipse(bluePen, curr.X * scale + positionAdjustment.X, curr.Y * scale + positionAdjustment.Y, 10, 10); g.DrawEllipse(greenPen, next.X * scale + positionAdjustment.X, next.Y * scale + positionAdjustment.Y, 10, 10); Font font = new Font(FontFamily.GenericSansSerif, 20, SystemFonts.DefaultFont.Style); for (int i = 0; i < remainingValues.Count; i++) { Point p1 = new Point((int)(remainingValues[i].X * scale + positionAdjustment.X), (int)(remainingValues[i].Y * scale + positionAdjustment.Y)); bool contains = false; for (int j = 0; j < result.Count; j++) { contains = result[j].Contains(new Vector2(remainingValues[i].X, remainingValues[i].Y)); if (contains) { break; } } if (remainingValues[i] != prev && remainingValues[i] != curr && remainingValues[i] != next && !contains) { g.DrawString(i.ToString(), font, Brushes.DarkGreen, new PointF(p1.X, p1.Y)); } } for (int i = 0; i < remainingValues.Count; i++) { Point p1 = new Point((int)(remainingValues[i].X * scale + positionAdjustment.X), (int)(remainingValues[i].Y * scale + positionAdjustment.Y)); if (remainingValues[i] == prev || remainingValues[i] == curr || remainingValues[i] == next) { g.DrawString(i.ToString(), font, Brushes.DarkViolet, new PointF(p1.X + 40, p1.Y)); } } }); } c++; } } //Resetting the shared points values for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result[i].Count; j++) { if (onesToReset.Contains(result[i][j])) { result[i][j] = new Vector2(result[i][j].X, result[i][j].Y - amountToRaise); } } } for (int r = 0; r < result.Count; r++) { VMFDebug.CreateDebugImage("FinalTriangulation" + r, onDraw: (g) => { float scale = 0.2f; Pen blackPen = new Pen(Color.Black, 3); Pen greyPen = new Pen(Color.Gray, 3); for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result[i].Count; j++) { int iN = (j + 1) % result[i].Count; Point p1 = new Point((int)(result[i][j].X * scale), (int)(result[i][j].Y * scale)); Point p2 = new Point((int)(result[i][iN].X * scale), (int)(result[i][iN].Y * scale)); g.DrawLine(greyPen, p1, p2); } } for (int i = 0; i < result[r].Count; i++) { int iN = (i + 1) % result[r].Count; Point p1 = new Point((int)(result[r][i].X * scale), (int)(result[r][i].Y * scale)); Point p2 = new Point((int)(result[r][iN].X * scale), (int)(result[r][iN].Y * scale)); g.DrawLine(blackPen, p1, p2); } }); } return(result); }
private void GenerateData(out List <Shape> shapesResult, out List <string> entities) { List <GenerationMethod> generationMethods = new List <GenerationMethod>(); entities = new List <string>(); EasyInputLayer.GetInput(out generationMethods, out entities); //Start shape construction List <Shape> shapes = new List <Shape>(); for (int i = 0; i < generationMethods.Count; i++) { shapes.AddRange(generationMethods[i].GetBrushes()); } List <Shape> finalShapeList = new List <Shape>(); for (int i = shapes.Count - 1; i >= 0; i--) { if (shapes[i] is Polygon) { (shapes[i] as Polygon).CalculatePreGenerateData(); if (!IsShapeConvex(shapes[i] as Polygon)) { VMFDebug.CreateDebugImage("PreTriangulation" + (i), onDraw: (g) => { Pen greyPen = new Pen(Color.Gray, 3); Pen redPen = new Pen(Color.Red, 3); for (int j = 0; j < shapes.Count; j++) { if (shapes[j] is Polygon) { VMFDebug.AddShapeToGraphics(g, shapes[j] as Polygon, greyPen); } } VMFDebug.AddShapeToGraphics(g, shapes[i] as Polygon, redPen); }); Console.WriteLine("Found a concave shape. Attempting triangulation"); List <Polygon> replacements = new List <Polygon>(); List <Shape> temp = ConvertToConvex(shapes[i] as Polygon); for (int j = 0; j < temp.Count; j++) { replacements.Add(temp[j] as Polygon); } Console.WriteLine("Single shape converted into " + replacements.Count + " new shapes"); List <Polygon> combinedShapes = new List <Polygon>(); for (int j = replacements.Count - 1; j >= 0; j--) { if (!IsShapeConvex(replacements[j] as Polygon)) { replacements[j] = RemoveRedundantPoints(replacements[j] as Polygon); Console.WriteLine("An invalid shape was found in the replacement batch. Attempting to fix..."); if (((PolygonShapeData)replacements[j].Data).PolygonPoints.Count < 3 || !IsShapeConvex(replacements[j] as Polygon)) { Console.WriteLine("Could not fix invalid shape. Deleting."); replacements.RemoveAt(j); } else { Console.WriteLine("Invalid shape fixed!"); } } } combinedShapes = replacements; CombineShapes(replacements, out combinedShapes); PolygonShapeData shapeData = shapes[i].Data as PolygonShapeData; for (int j = 0; j < combinedShapes.Count; j++) { finalShapeList.Add(combinedShapes[j]); } } else { finalShapeList.Add(shapes[i] as Polygon); } } else { finalShapeList.Add(shapes[i]); } } int currId = 0; for (int i = 0; i < finalShapeList.Count; i++) { finalShapeList[i].ID = i; finalShapeList[i].GenerateSides(currId); currId += finalShapeList[i].Sides.Length; } //End shape construction shapesResult = finalShapeList; }
public static void CombineShapes(List <Polygon> shapes, out List <Polygon> resultingShapes, int depth = 0) { List <Polygon> applicants = new List <Polygon>(); Polygon currentPolygonToEval = null; List <Polygon> options = new List <Polygon>(shapes); int save = 0; while (true) { if (currentPolygonToEval == null) { if (options.Count <= 0) { break; } currentPolygonToEval = options[0]; options.RemoveAt(0); } Polygon prev = null; Polygon found = null; bool anyCombination = false; for (int i = 0; i < options.Count; i++) { Polygon v = new Polygon(currentPolygonToEval); v = v.Combine(options[i]); if (v != null) { v = RemoveRedundantPoints(v); if (IsShapeConvex(v)) { found = options[i]; prev = new Polygon(currentPolygonToEval); currentPolygonToEval = v; options.RemoveAt(i); anyCombination = true; break; } } } VMFDebug.CreateDebugImage("CombiningStep" + (save++), onDraw: (g) => { Pen greyPen = new Pen(Color.Gray, 3); Pen blackPen = new Pen(Color.Black, 3); Pen redPen = new Pen(Color.Red, 3); Pen bluePen = new Pen(Color.Blue, 3); Pen greenPen = new Pen(Color.Green, 3); for (int i = 0; i < options.Count; i++) { VMFDebug.AddShapeToGraphics(g, options[i], greyPen); } for (int i = 0; i < applicants.Count; i++) { VMFDebug.AddShapeToGraphics(g, applicants[i], blackPen); } if (found != null) { VMFDebug.AddShapeToGraphics(g, found, bluePen); } if (prev != null) { VMFDebug.AddShapeToGraphics(g, prev, greenPen); } else { VMFDebug.AddShapeToGraphics(g, currentPolygonToEval, redPen); } }); if (!anyCombination) { applicants.Add(new Polygon(currentPolygonToEval)); currentPolygonToEval = null; } } List <Polygon> result = new List <Polygon>(); for (int i = 0; i < applicants.Count; i++) { bool canAdd = true; for (int j = 0; j < result.Count; j++) { PolygonShapeData rSD = result[j].Data as PolygonShapeData; PolygonShapeData aSD = applicants[i].Data as PolygonShapeData; if (rSD.PolygonPoints.SequenceEqual(aSD.PolygonPoints)) { canAdd = false; break; } } if (canAdd) { result.Add(applicants[i]); } } resultingShapes = result; }
public static List <Polygon> CreateWalls(Polygon polygon, WallData wallData) { List <List <Vector2> > allPoints = new List <List <Vector2> >(); List <Vector2> points = new List <Vector2>(); List <Vector2> lastFace = new List <Vector2>(); List <List <Vector2> > normals = new List <List <Vector2> >(); PolygonShapeData polyData = polygon.Data as PolygonShapeData; List <Vector2> sharedValues = polyData.PolygonPoints.GroupBy(x => x).Where(g => g.Count() > 1).Select(x => x.Key).ToList(); for (int i = 0; i < polyData.PolygonPoints.Count; i++) { if (sharedValues.Contains(polyData.PolygonPoints[i])) { wallData.facesIndicesToSkip.Add(i); i++; } } for (int i = 0; i < polyData.PolygonPoints.Count; i++) { if (wallData.facesIndicesToSkip.Contains(i)) { continue; } //Okay basically all the shit I'm doing here is getting the vertex normal of the current point //and basing how the wall should be made based off that, so all the walls end up as trapazoids. //The only exception is if a wall piece is missing, I then get the intersection based on which //side of the wall it is, and try to flatten it. It's not perfect but it works? int index1 = (i - 1) % polyData.PolygonPoints.Count; if (index1 == -1) { index1 = polyData.PolygonPoints.Count - 1; } int index2 = i % polyData.PolygonPoints.Count; int index3 = (i + 1) % polyData.PolygonPoints.Count; int index4 = (i + 2) % polyData.PolygonPoints.Count; Vector2 a = polyData.PolygonPoints[index1] * polyData.Scalar; Vector2 b = polyData.PolygonPoints[index2] * polyData.Scalar; Vector2 c = polyData.PolygonPoints[index3] * polyData.Scalar; Vector2 d = polyData.PolygonPoints[index4] * polyData.Scalar; Vector2 abNormal = Vector2.Normalize(Shape.GetNormal2D(a, b)); Vector2 bcNormal = Vector2.Normalize(Shape.GetNormal2D(b, c)); Vector2 vertexNormalabc = Vector2.Normalize((abNormal + bcNormal)) * wallData.Thickness; Vector2 cdNormal = Vector2.Normalize(Shape.GetNormal2D(c, d)); Vector2 vertexNormalbcd = Vector2.Normalize((bcNormal + cdNormal)) * wallData.Thickness; Vector2 point = polyData.PolygonPoints[index2] * polyData.Scalar + vertexNormalabc; Vector2 point2 = polyData.PolygonPoints[index3] * polyData.Scalar + vertexNormalbcd; points.Add(point); points.Add(point2); points.Add(c); points.Add(b); if (wallData.facesIndicesToSkip.Contains(i + 1)) { LineEquation first = new LineEquation(points[2], points[2] - bcNormal); LineEquation second = new LineEquation(points[0], points[1]); Vector2 intersectPoint; if (first.IntersectsWithLine(second, out intersectPoint)) { points[1] = intersectPoint; } } if (wallData.facesIndicesToSkip.Contains(i - 1)) { LineEquation first = new LineEquation(points[3], points[3] - bcNormal); LineEquation second = new LineEquation(points[0], points[1]); Vector2 intersectPoint; if (first.IntersectsWithLine(second, out intersectPoint)) { points[0] = intersectPoint; } } allPoints.Add(points); points = new List <Vector2>(); } //Idk there were edge cases I had NAN values and they didnt seem to affect anything sooooooo for (int i = allPoints.Count - 1; i >= 0; i--) { bool hasNan = false; for (int j = 0; j < allPoints[i].Count; j++) { if (allPoints[i][j] != allPoints[i][j]) { hasNan = true; break; } } if (hasNan) { allPoints.RemoveAt(i); } } VMFDebug.CreateDebugImage("WallOutput", onDraw: (g) => { float scale = 0.1f; for (int i = 0; i < allPoints.Count; i++) { for (int j = 0; j < allPoints[i].Count; j++) { int iN = (j + 1) % allPoints[i].Count; Point p1 = new Point((int)(allPoints[i][j].X * scale + 100), (int)(allPoints[i][j].Y * scale + 100)); Point p2 = new Point((int)(allPoints[i][iN].X * scale + 100), (int)(allPoints[i][iN].Y * scale + 100)); g.DrawLine(new Pen(Color.Black, 3), p1, p2); } } }); List <Polygon> finalPolygons = new List <Polygon>(); for (int i = 0; i < allPoints.Count; i++) { finalPolygons.Add( new Polygon() { Position = polygon.Position + new Vector3(0, 0, wallData.Height * 0.5f), Data = new PolygonShapeData() { Depth = wallData.Height + ((PolygonShapeData)polygon.Data).Depth, Scalar = 1, PolygonPoints = allPoints[i] } }); } return(finalPolygons); }