Beispiel #1
0
        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);
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        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;
        }
Beispiel #4
0
        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;
        }
Beispiel #5
0
        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);
        }