<commit_before>﻿using UnityEngine;
using System;
using System.Collections.Generic;
using RemoteFortressReader;
using DFHack;

public class VoxelGenerator
{
    MapDataStore map;
    public VoxelGenerator(MapDataStore map)
    {
        this.map = map;
    }

    private List<Vector3> vertices = new List<Vector3>();
    private List<Vector2> uvs = new List<Vector2>();
    private List<Color> colors = new List<Color>();
    private List<int> triangles = new List<int>();

    bool OpenedDiagonals { get { return false; } }

    const float bottomlessDepth = -1000000;

    public bool bottomless = false;

    public enum CornerType
    {
        Diamond,
        Square,
        Rounded
    }

    CornerType UsedCornerType { get { return GameSettings.Instance.meshing.cornerType; } }

    public CPUMesh TerrainMesh
    {
        get
        {
            Triangulate();
            return new CPUMesh(vertices.ToArray(), null, null, uvs.ToArray(), null, null, null, triangles.ToArray());
        }
    }

    [Flags]
    enum Directions
    {
        None = 0,
        NorthWest = 1,
        NorthEast = 2,
        SouthWest = 4,
        SouthEast = 8,

        North = NorthWest | NorthEast,
        South = SouthWest | SouthEast,
        East = NorthEast | SouthEast,
        West = NorthWest | SouthWest,

        All = NorthWest | NorthEast | SouthWest | SouthEast
    }

    Directions RotateCW(Directions dir)
    {
        Directions output = Directions.None;
        if ((dir & Directions.NorthWest) == Directions.NorthWest)
            output |= Directions.NorthEast;
        if ((dir & Directions.NorthEast) == Directions.NorthEast)
            output |= Directions.SouthEast;
        if ((dir & Directions.SouthEast) == Directions.SouthEast)
            output |= Directions.SouthWest;
        if ((dir & Directions.SouthWest) == Directions.SouthWest)
            output |= Directions.NorthWest;
        return output;
    }

    Directions Rotate180(Directions dir)
    {
        return RotateCW(RotateCW(dir));
    }

    Directions RotateCCW(Directions dir)
    {
        return RotateCW(RotateCW(RotateCW(dir)));
    }

    private void Triangulate()
    {
        vertices.Clear();
        uvs.Clear();
        colors.Clear();
        triangles.Clear();

        TriangulateCellRows();
    }

    private void TriangulateCellRows()
    {
        for (int y = map.MinCoord.y; y < map.MaxCoord.y - 1; y++)
        {
            for (int x = map.MinCoord.x; x < map.MaxCoord.x - 1; x++)
            {
                int z = map.SliceOrigin.z;
                Directions edges = Directions.None;
                if (x == map.MinCoord.x)
                    edges |= Directions.West;
                if (x == map.MaxCoord.x - 2)
                    edges |= Directions.East;
                if (y == map.MinCoord.y)
                    edges |= Directions.North;
                if (y == map.MaxCoord.y - 2)
                    edges |= Directions.South;

                TriangulateCell(
                    map[x, y, z],
                    map[x + 1, y, z],
                    map[x, y + 1, z],
                    map[x + 1, y + 1, z],
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x - 1, y - 1, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x, y - 1, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x, y, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x - 1, y, z))),
                    edges);
            }
        }
    }

    #region Tile Test Conditions
    public static bool UseBoth(MapDataStore.Tile tile)
    {
        if (tile == null)
            return true; //means it's air/empty
        switch (tile.shape)
        {
            case TiletypeShape.PEBBLES:
            case TiletypeShape.BOULDER:
            case TiletypeShape.SAPLING:
            case TiletypeShape.SHRUB:
                break;
            default:
                return false;
        }
        return true;
    }

    public static bool IsNatural(MapDataStore.Tile tile)
    {
        switch (tile.tiletypeMaterial)
        {
            case TiletypeMaterial.NO_MATERIAL:
            case TiletypeMaterial.AIR:
            case TiletypeMaterial.SOIL:
            case TiletypeMaterial.STONE:
            case TiletypeMaterial.FEATURE:
            case TiletypeMaterial.LAVA_STONE:
            case TiletypeMaterial.MINERAL:
            case TiletypeMaterial.GRASS_LIGHT:
            case TiletypeMaterial.GRASS_DARK:
            case TiletypeMaterial.GRASS_DRY:
            case TiletypeMaterial.GRASS_DEAD:
            case TiletypeMaterial.PLANT:
            case TiletypeMaterial.POOL:
            case TiletypeMaterial.BROOK:
            case TiletypeMaterial.RIVER:
            case TiletypeMaterial.ROOT:
            case TiletypeMaterial.HFS:
                return true;
            default:
                return false;
        }
    }

    public static bool HandleShape(MapDataStore.Tile tile)
    {
        switch (tile.shape)
        {
            case TiletypeShape.NO_SHAPE:
            case TiletypeShape.EMPTY:
            case TiletypeShape.FLOOR:
            case TiletypeShape.WALL:
            case TiletypeShape.BROOK_TOP:
            case TiletypeShape.ENDLESS_PIT:
                return true;
            default:
                return false;
        }

    }

    public static bool Handled(MapDataStore.Tile tile)
    {
        if (UseBoth(tile))
            return true;
        if (tile == null)
            return true; //means it's air/empty
        if (tile.Hidden)
            return false;
        return (HandleShape(tile) && IsNatural(tile));
    }

    public static bool IsFloor(MapDataStore.Tile tile)
    {
        switch (tile.shape)
        {
            case TiletypeShape.FLOOR:
            case TiletypeShape.BOULDER:
            case TiletypeShape.PEBBLES:
            case TiletypeShape.SAPLING:
            case TiletypeShape.SHRUB:
                return true;
            default:
                return false;
        }
    }
    #endregion

    private void TriangulateCell(
        MapDataStore.Tile northWest,
        MapDataStore.Tile northEast,
        MapDataStore.Tile southWest, 
        MapDataStore.Tile southEast,
        Vector3 northWestPoint,
        Vector3 northEastPoint,
        Vector3 southEastPoint,
        Vector3 southWestPoint,
        Directions edges)
    {
        var corner = UsedCornerType;
        if (!Handled(northWest)
            || !Handled(northEast)
            || !Handled(southWest)
            || !Handled(southEast)
            )
            corner = CornerType.Square;

        Directions walls = Directions.None;
        if (northWest != null && northWest.shape == TiletypeShape.WALL && Handled(northWest))
        {
            walls |= Directions.NorthWest;
        }
        if (northEast != null && northEast.shape == TiletypeShape.WALL && Handled(northEast))
        {
            walls |= Directions.NorthEast;
        }
        if (southWest != null && southWest.shape == TiletypeShape.WALL && Handled(southWest))
        {
            walls |= Directions.SouthWest;
        }
        if (southEast != null && southEast.shape == TiletypeShape.WALL && Handled(southEast))
        {
            walls |= Directions.SouthEast;
        }

        Directions wallFloors = walls;
        if (northWest != null && IsFloor(northWest) && Handled(northWest))
        {
            wallFloors |= Directions.NorthWest;
        }
        if (northEast != null && IsFloor(northEast) && Handled(northEast))
        {
            wallFloors |= Directions.NorthEast;
        }
        if (southWest != null && IsFloor(southWest) && Handled(southWest))
        {
            wallFloors |= Directions.SouthWest;
        }
        if (southEast != null && IsFloor(southEast) && Handled(southEast))
        {
            wallFloors |= Directions.SouthEast;
        }

        switch (wallFloors)
        {
            case Directions.None:
                return;
            case Directions.NorthWest:
            case Directions.North:
            case Directions.North | Directions.SouthWest:
            case Directions.NorthWest | Directions.SouthEast:
            case Directions.All:
                AddRotatedCell(
                    northWestPoint,
                    northEastPoint,
                    southEastPoint,
                    southWestPoint,
                    wallFloors, edges, walls, corner);
                break;
            case Directions.NorthEast:
            case Directions.East:
            case Directions.North | Directions.SouthEast:
            case Directions.NorthEast | Directions.SouthWest:
                AddRotatedCell(
                    northEastPoint,
                    southEastPoint,
                    southWestPoint,
                    northWestPoint,
                  RotateCCW(wallFloors), RotateCCW(edges), RotateCCW(walls), corner);
                break;
            case Directions.SouthEast:
            case Directions.South:
            case Directions.NorthEast | Directions.South:
                AddRotatedCell(
                    southEastPoint,
                    southWestPoint,
                    northWestPoint,
                    northEastPoint,
                    Rotate180(wallFloors), Rotate180(edges), Rotate180(walls), corner);
                break;
            case Directions.SouthWest:
            case Directions.West:
            case Directions.West | Directions.SouthEast:
                AddRotatedCell(
                    southWestPoint,
                    northWestPoint,
                    northEastPoint,
                    southEastPoint,
                    RotateCW(wallFloors), RotateCW(edges), RotateCW(walls), corner);
                break;
        }
    }

    private void AddRotatedCell(
        Vector3 northWest,
        Vector3 northEast,
        Vector3 southEast,
        Vector3 southWest,
        Directions neighbors,
        Directions edges,
        Directions walls,
        CornerType corner)
    {
        Vector3 north = (northWest + northEast) / 2;
        Vector3 east = (northEast + southEast) / 2;
        Vector3 south = (southEast + southWest) / 2;
        Vector3 west = (southWest + northWest) / 2;
        Vector3 center = (north + south) / 2;
        switch (neighbors)
        {
            case Directions.NorthWest:
                AddOuterCornerCell(west, northWest, north, center, corner, edges, walls);
                break;
            case Directions.North:
                AddStraightCell(west, northWest, north, northEast, east, center, corner, edges, walls);
                break;
            case Directions.North | Directions.West:
                AddInnerCornerCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.NorthWest | Directions.SouthEast:
                AddSaddleCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.All:
                AddCenterCell(north, east, south, west, center, corner, edges, walls);
                break;
            default:
                break;
        }
    }

    enum WallType
    {
        None,
        Floor,
        Wall,
        Both
    }

    void AddOuterCornerCell(
        Vector3 west,
        Vector3 northWest,
        Vector3 north,
        Vector3 center,
        CornerType corner, 
        Directions edges,
        Directions walls
        )
    {
        if ((edges & Directions.NorthWest) != Directions.NorthWest)
        {
            if ((walls & Directions.NorthWest) == Directions.NorthWest)
                AddCornerMesh(west, north, center, northWest, corner, WallType.Both);
            else
                AddCornerMesh(west, north, center, northWest, corner, WallType.Floor);
        }
    }

    void AddStraightCell(
        Vector3 west,
        Vector3 northWest,
        Vector3 north, 
        Vector3 northEast,
        Vector3 east,
        Vector3 center, 
        CornerType corner, 
        Directions edges, 
        Directions walls
        )
    {
        switch (walls)
        {
            case Directions.North:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Both, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Both, false, true);
                break;
            case Directions.NorthWest:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, corner, WallType.Both, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Floor, false, true);
                break;
            case Directions.NorthEast:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Floor, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, corner, WallType.Both, false, true);
                break;
            default:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Floor, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Floor, false, true);
                break;
        }
    }

    void AddInnerCornerCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (edges)
        {
            //case Directions.East:
            //    AddStraightCell(south, west, north, center, (walls == Directions.North) ? CornerType.Square : corner, RotateCW(edges), RotateCW(walls) & Directions.North);
            //    break;
            //case Directions.South:
            //    AddStraightCell(west, north, east, center, (walls == Directions.West) ? CornerType.Square : corner, edges, walls & Directions.North);
            //    break;
            case Directions.North:
                switch (walls)
                {
                    case Directions.SouthWest:
                    case Directions.SouthWest | Directions.NorthEast:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddStraight(east, west, WallType.Floor);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, west, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.West:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, west, center, CornerType.Square, WallType.Both);
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.Floor);
                        }
                        break;
                    case Directions.West | Directions.North:
                        AddCorner(south, east, center, corner, WallType.Both);
                        AddStraight(east, west, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.North:
                    default:
                        AddCorner(south, east, center, corner, WallType.Floor);
                        AddStraight(east, west, WallType.Floor);
                        break;
                }
                break;
            case Directions.West:
                switch (walls)
                {
                    case Directions.NorthEast:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddStraight(north, south, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(north, east, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.North:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, south, center, CornerType.Square, WallType.None);
                            AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        }
                        break;
                    case Directions.North | Directions.West:
                        AddStraight(north, south, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    case Directions.SouthWest:
                    case Directions.West:
                    default:
                        AddStraight(north, south, WallType.Floor);
                        AddCorner(south, east, center, corner, WallType.Floor);
                        break;
                }
                break;
            case Directions.North | Directions.West:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                        if (corner != CornerType.Square)
                        {
                            AddCorner(south, east, center, corner, WallType.Both);
                            AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.SouthWest:
                    case Directions.North:
                    case Directions.West:
                    case Directions.NorthEast | Directions.SouthWest:
                    default:
                        if (corner != CornerType.Square)
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                }
                break;
            case Directions.North | Directions.East:
                switch (walls)
                {
                    case Directions.West:
                    case Directions.North | Directions.West:
                        AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.SouthWest:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                            AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        else
                        {
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.North:
                    default:
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                    case Directions.North:
                    case Directions.West:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthWest:
                        if (corner == CornerType.Square)
                            AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        else
                        {
                            AddCorner(west, north, center, corner, WallType.Wall);
                            AddCorner(north, west, center, corner, WallType.None);
                            AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthEast:
                    case Directions.SouthWest:
                    case Directions.NorthEast | Directions.SouthWest:
                    default:
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.West | Directions.South:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                    case Directions.North:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthEast:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                            AddCorner(north, east, center, corner, WallType.Both);
                        else
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.SouthWest:
                    case Directions.West:
                    default:
                        AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            default:
                {
                    switch (walls)
                    {
                        case Directions.NorthWest:
                            AddCorner(west, north, center, corner, WallType.Wall);
                            AddCorner(north, west, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.NorthEast:
                            AddCorner(north, east, center, corner, WallType.Wall);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.SouthWest:
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.North:
                            AddStraight(west, east, WallType.Wall);
                            AddStraight(east, west, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.West:
                            AddStraight(south, north, WallType.Wall);
                            AddStraight(north, south, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.NorthEast | Directions.SouthWest:
                            AddCorner(north, east, center, corner, WallType.Wall);
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, north, center, corner, WallType.None);
                            break;
                        case Directions.North | Directions.West:
                            AddCorner(south, east, center, corner, WallType.Both);
                            break;
                        default:
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                    }
                }
                break;
        }
    }

    void AddSaddleCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        if (OpenedDiagonals)
            corner = CornerType.Diamond;
        switch (edges)
        {
            case Directions.East | Directions.North:
            case Directions.West | Directions.South:
                break;
            case Directions.North:
            case Directions.West:
            case Directions.North | Directions.West:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        break;
                    default:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        break;
                }
                break;
            case Directions.East:
            case Directions.South:
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    default:
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                }
                break;
            default:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    case Directions.NorthWest:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    case Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                    default:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                }
                break;
        }
    }

    void AddCenterCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (walls)
        {
            case Directions.None:
            case Directions.NorthWest:
            case Directions.North:
            case Directions.North | Directions.SouthWest:
            case Directions.NorthWest | Directions.SouthEast:
            case Directions.All:
                AddRotatedCenterCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.NorthEast:
            case Directions.East:
            case Directions.North | Directions.SouthEast:
            case Directions.NorthEast | Directions.SouthWest:
                AddRotatedCenterCell(east, south, west, north, center, corner, RotateCCW(edges), RotateCCW(walls));
                break;
            case Directions.SouthEast:
            case Directions.South:
            case Directions.NorthEast | Directions.South:
                AddRotatedCenterCell(south, west, north, east, center, corner, Rotate180(edges), Rotate180(walls));
                break;
            case Directions.SouthWest:
            case Directions.West:
            case Directions.West | Directions.SouthEast:
                AddRotatedCenterCell(west, north, east, south, center, corner, RotateCW(edges), RotateCW(walls));
                break;
        }
    }

    void AddRotatedCenterCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (edges)
        {
            case Directions.North:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                            AddCorner(east, south, center, corner, WallType.Floor);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Wall);
                            AddStraight(east, west, WallType.Both);
                            AddCorner(east, south, center, corner, WallType.None);
                        }
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddStraight(east, west, WallType.Floor);
                        break;
                    case Directions.All:
                        AddStraight(east, west, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    default:
                        AddStraight(east, west, WallType.Floor);
                        break;
                }
                break;
            case Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddStraight(west, east, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddStraight(west, east, WallType.Floor);
                        break;
                    case Directions.West | Directions.North:
                    case Directions.North:
                    case Directions.All:
                        AddStraight(west, east, WallType.Both);
                        break;
                    default:
                        AddStraight(west, east, WallType.Floor);
                        break;
                }
                break;
            case Directions.East:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddStraight(south, north, WallType.Floor);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.North:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddStraight(south, north, WallType.Floor);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddStraight(south, north, WallType.Both);
                        break;
                    default:
                        AddStraight(south, north, WallType.Floor);
                        break;
                }
                break;
            case Directions.West:
                switch (walls)
                {
                    case Directions.North:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.West | Directions.North:
                        AddStraight(north, south, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.Wall);
                        AddCorner(east, south, center, corner, WallType.None);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;
                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddStraight(north, south, WallType.Floor);
                        break;
                    case Directions.All:
                        AddStraight(north, south, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    default:
                        AddStraight(north, south, WallType.Floor);
                        break;
                }
                break;
            case Directions.West | Directions.North:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                        AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddCorner(east, south, center, corner, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(east, south, center, corner, WallType.Wall);
                        }
                        else
                        {
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.None);
                            AddCorner(east, south, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.All:
                        AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    default:
                        AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.North | Directions.East:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    case Directions.NorthWest | Directions.SouthEast:
                    default:
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.North:
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        break;
                    default:
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.South | Directions.West:
                switch (walls)
                {
                    case Directions.North:
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                    default:
                        AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            default:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.North:
                        AddStraight(west, east, WallType.Wall);
                        AddStraight(east, west, WallType.None);
                        break;
                    case Directions.West | Directions.North:
                        AddCorner(south, east, center, corner, WallType.Wall);
                        AddCorner(east, south, center, corner, WallType.None);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);

                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        break;
                    default:
                        break;
                }
                break;
        }
    }

    private Vector3[] GetCornerPoints(Vector3 start, Vector3 end, Vector3 center, CornerType type)
    {
        switch (type)
        {
            case CornerType.Square:
                return new Vector3[]
                {
                    start,
                    center,
                    end
                };
            case CornerType.Rounded:
                return new Vector3[] 
                {
                    start,
                    (start + center) / 2,
                    (end + center) / 2,
                    end
                };
            default:
                return new Vector3[]
                {
                    start,
                    end
                };
        }
    }

    private void AddMicroCell(CornerType type, Vector3 center, Vector3 start, Vector3 end, Vector3 corner,  float centerHeight, float cornerHeight, bool startWall, bool endWall)
    {
        if(centerHeight == cornerHeight)
        {
            AddHorizontalPoly(centerHeight, false, center, start, corner, end);
            AddHorizontalPoly(bottomless ? bottomlessDepth : 0, true, center, start, corner, end);
            return;
        }
        switch (type)
        {
            case CornerType.Square:
                AddHorizontalPoly(centerHeight, false, center, start, corner, end);
                AddHorizontalPoly(bottomless ? bottomlessDepth : 0, true, center, start, corner, end);
                break;
            case CornerType.Rounded:
                break;
            default:
                if (centerHeight > 0.001f)
                {
                    AddHorizontalPoly(centerHeight, false, center, start, end);
                    if(cornerHeight <= 0.00f)
                        AddHorizontalPoly(bottomless ? bottomlessDepth : 0, true, center, start, end);
                    else
                        AddHorizontalPoly(bottomless ? bottomlessDepth : 0, true, center, start, corner, end);
                }
                if (cornerHeight > 0.001f)
                {
                    AddHorizontalPoly(cornerHeight, false, corner, end, center);
                    if (centerHeight <= 0.00f)
                        AddHorizontalPoly(bottomless ? bottomlessDepth : 0, false, corner, end, center);
                }
                if (centerHeight > cornerHeight)
                    AddWallMesh(-0.5f, 0.5f, centerHeight, bottomless ? bottomlessDepth : 0, start, end);
                else
                    AddWallMesh(-0.5f, 0.5f, cornerHeight, bottomless ? bottomlessDepth : 0, end, start);
                break;
        }
    }

    private void AddCornerMesh(Vector3 start, Vector3 end, Vector3 center, Vector3 corner, CornerType type, WallType wallType, bool startWalls = true, bool endWalls = true)
    {
        switch (type)
        {
            case CornerType.Diamond:
                break;
            case CornerType.Square:<commit_msg>Revert "more freaking voxel work"<commit_after>﻿using UnityEngine;
using System;
using System.Collections.Generic;
using RemoteFortressReader;
using DFHack;

public class VoxelGenerator
{
    MapDataStore map;
    public VoxelGenerator(MapDataStore map)
    {
        this.map = map;
    }

    private List<Vector3> vertices = new List<Vector3>();
    private List<Vector2> uvs = new List<Vector2>();
    private List<Color> colors = new List<Color>();
    private List<int> triangles = new List<int>();

    bool OpenedDiagonals { get { return false; } }

    const float bottomlessDepth = -1000000;

    public bool bottomless = false;

    public enum CornerType
    {
        Diamond,
        Square,
        Rounded
    }

    CornerType UsedCornerType { get { return GameSettings.Instance.meshing.cornerType; } }

    public CPUMesh TerrainMesh
    {
        get
        {
            Triangulate();
            return new CPUMesh(vertices.ToArray(), null, null, uvs.ToArray(), null, null, null, triangles.ToArray());
        }
    }

    [Flags]
    enum Directions
    {
        None = 0,
        NorthWest = 1,
        NorthEast = 2,
        SouthWest = 4,
        SouthEast = 8,

        North = NorthWest | NorthEast,
        South = SouthWest | SouthEast,
        East = NorthEast | SouthEast,
        West = NorthWest | SouthWest,

        All = NorthWest | NorthEast | SouthWest | SouthEast
    }

    Directions RotateCW(Directions dir)
    {
        Directions output = Directions.None;
        if ((dir & Directions.NorthWest) == Directions.NorthWest)
            output |= Directions.NorthEast;
        if ((dir & Directions.NorthEast) == Directions.NorthEast)
            output |= Directions.SouthEast;
        if ((dir & Directions.SouthEast) == Directions.SouthEast)
            output |= Directions.SouthWest;
        if ((dir & Directions.SouthWest) == Directions.SouthWest)
            output |= Directions.NorthWest;
        return output;
    }

    Directions Rotate180(Directions dir)
    {
        return RotateCW(RotateCW(dir));
    }

    Directions RotateCCW(Directions dir)
    {
        return RotateCW(RotateCW(RotateCW(dir)));
    }

    private void Triangulate()
    {
        vertices.Clear();
        uvs.Clear();
        colors.Clear();
        triangles.Clear();

        TriangulateCellRows();
    }

    private void TriangulateCellRows()
    {
        for (int y = map.MinCoord.y; y < map.MaxCoord.y - 1; y++)
        {
            for (int x = map.MinCoord.x; x < map.MaxCoord.x - 1; x++)
            {
                int z = map.SliceOrigin.z;
                Directions edges = Directions.None;
                if (x == map.MinCoord.x)
                    edges |= Directions.West;
                if (x == map.MaxCoord.x - 2)
                    edges |= Directions.East;
                if (y == map.MinCoord.y)
                    edges |= Directions.North;
                if (y == map.MaxCoord.y - 2)
                    edges |= Directions.South;

                TriangulateCell(
                    map[x, y, z],
                    map[x + 1, y, z],
                    map[x, y + 1, z],
                    map[x + 1, y + 1, z],
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x - 1, y - 1, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x, y - 1, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x, y, z))),
                    GameMap.DFtoUnityCoord(map.WorldToLocalSpace(new DFCoord(x - 1, y, z))),
                    edges);
            }
        }
    }


    public static bool UseBoth(MapDataStore.Tile tile)
    {
        if (tile == null)
            return true; //means it's air/empty
        switch (tile.shape)
        {
            case TiletypeShape.PEBBLES:
            case TiletypeShape.BOULDER:
            case TiletypeShape.SAPLING:
            case TiletypeShape.SHRUB:
                break;
            default:
                return false;
        }
        return true;
    }

    public static bool IsNatural(MapDataStore.Tile tile)
    {
        switch (tile.tiletypeMaterial)
        {
            case TiletypeMaterial.NO_MATERIAL:
            case TiletypeMaterial.AIR:
            case TiletypeMaterial.SOIL:
            case TiletypeMaterial.STONE:
            case TiletypeMaterial.FEATURE:
            case TiletypeMaterial.LAVA_STONE:
            case TiletypeMaterial.MINERAL:
            case TiletypeMaterial.GRASS_LIGHT:
            case TiletypeMaterial.GRASS_DARK:
            case TiletypeMaterial.GRASS_DRY:
            case TiletypeMaterial.GRASS_DEAD:
            case TiletypeMaterial.PLANT:
            case TiletypeMaterial.POOL:
            case TiletypeMaterial.BROOK:
            case TiletypeMaterial.RIVER:
            case TiletypeMaterial.ROOT:
            case TiletypeMaterial.HFS:
                return true;
            default:
                return false;
        }
    }

    public static bool HandleShape(MapDataStore.Tile tile)
    {
        switch (tile.shape)
        {
            case TiletypeShape.NO_SHAPE:
            case TiletypeShape.EMPTY:
            case TiletypeShape.FLOOR:
            case TiletypeShape.WALL:
            case TiletypeShape.BROOK_TOP:
            case TiletypeShape.ENDLESS_PIT:
                return true;
            default:
                return false;
        }

    }

    public static bool Handled(MapDataStore.Tile tile)
    {
        if (UseBoth(tile))
            return true;
        if (tile == null)
            return true; //means it's air/empty
        if (tile.Hidden)
            return false;
        return (HandleShape(tile) && IsNatural(tile));
    }

    public static bool IsFloor(MapDataStore.Tile tile)
    {
        switch (tile.shape)
        {
            case TiletypeShape.FLOOR:
            case TiletypeShape.BOULDER:
            case TiletypeShape.PEBBLES:
            case TiletypeShape.SAPLING:
            case TiletypeShape.SHRUB:
                return true;
            default:
                return false;
        }
    }

    private void TriangulateCell(
        MapDataStore.Tile northWest,
        MapDataStore.Tile northEast,
        MapDataStore.Tile southWest, 
        MapDataStore.Tile southEast,
        Vector3 northWestPoint,
        Vector3 northEastPoint,
        Vector3 southEastPoint,
        Vector3 southWestPoint,
        Directions edges)
    {
        var corner = UsedCornerType;
        if (!Handled(northWest)
            || !Handled(northEast)
            || !Handled(southWest)
            || !Handled(southEast)
            )
            corner = CornerType.Square;

        Directions walls = Directions.None;
        if (northWest != null && northWest.shape == TiletypeShape.WALL && Handled(northWest))
        {
            walls |= Directions.NorthWest;
        }
        if (northEast != null && northEast.shape == TiletypeShape.WALL && Handled(northEast))
        {
            walls |= Directions.NorthEast;
        }
        if (southWest != null && southWest.shape == TiletypeShape.WALL && Handled(southWest))
        {
            walls |= Directions.SouthWest;
        }
        if (southEast != null && southEast.shape == TiletypeShape.WALL && Handled(southEast))
        {
            walls |= Directions.SouthEast;
        }

        Directions wallFloors = walls;
        if (northWest != null && IsFloor(northWest) && Handled(northWest))
        {
            wallFloors |= Directions.NorthWest;
        }
        if (northEast != null && IsFloor(northEast) && Handled(northEast))
        {
            wallFloors |= Directions.NorthEast;
        }
        if (southWest != null && IsFloor(southWest) && Handled(southWest))
        {
            wallFloors |= Directions.SouthWest;
        }
        if (southEast != null && IsFloor(southEast) && Handled(southEast))
        {
            wallFloors |= Directions.SouthEast;
        }

        switch (wallFloors)
        {
            case Directions.None:
                return;
            case Directions.NorthWest:
            case Directions.North:
            case Directions.North | Directions.SouthWest:
            case Directions.NorthWest | Directions.SouthEast:
            case Directions.All:
                AddRotatedCell(
                    northWestPoint,
                    northEastPoint,
                    southEastPoint,
                    southWestPoint,
                    wallFloors, edges, walls, corner);
                break;
            case Directions.NorthEast:
            case Directions.East:
            case Directions.North | Directions.SouthEast:
            case Directions.NorthEast | Directions.SouthWest:
                AddRotatedCell(
                    northEastPoint,
                    southEastPoint,
                    southWestPoint,
                    northWestPoint,
                  RotateCCW(wallFloors), RotateCCW(edges), RotateCCW(walls), corner);
                break;
            case Directions.SouthEast:
            case Directions.South:
            case Directions.NorthEast | Directions.South:
                AddRotatedCell(
                    southEastPoint,
                    southWestPoint,
                    northWestPoint,
                    northEastPoint,
                    Rotate180(wallFloors), Rotate180(edges), Rotate180(walls), corner);
                break;
            case Directions.SouthWest:
            case Directions.West:
            case Directions.West | Directions.SouthEast:
                AddRotatedCell(
                    southWestPoint,
                    northWestPoint,
                    northEastPoint,
                    southEastPoint,
                    RotateCW(wallFloors), RotateCW(edges), RotateCW(walls), corner);
                break;
        }
    }

    private void AddRotatedCell(
        Vector3 northWest,
        Vector3 northEast,
        Vector3 southEast,
        Vector3 southWest,
        Directions neighbors,
        Directions edges,
        Directions walls,
        CornerType corner)
    {
        Vector3 north = (northWest + northEast) / 2;
        Vector3 east = (northEast + southEast) / 2;
        Vector3 south = (southEast + southWest) / 2;
        Vector3 west = (southWest + northWest) / 2;
        Vector3 center = (north + south) / 2;
        switch (neighbors)
        {
            case Directions.NorthWest:
                AddOuterCornerCell(west, northWest, north, center, corner, edges, walls);
                break;
            case Directions.North:
                AddStraightCell(west, northWest, north, northEast, east, center, corner, edges, walls);
                break;
            case Directions.North | Directions.West:
                AddInnerCornerCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.NorthWest | Directions.SouthEast:
                AddSaddleCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.All:
                AddCenterCell(north, east, south, west, center, corner, edges, walls);
                break;
            default:
                break;
        }
    }

    enum WallType
    {
        None,
        Floor,
        Wall,
        Both
    }

    void AddOuterCornerCell(
        Vector3 west,
        Vector3 northWest,
        Vector3 north,
        Vector3 center,
        CornerType corner, 
        Directions edges,
        Directions walls
        )
    {
        if ((edges & Directions.NorthWest) != Directions.NorthWest)
        {
            if ((walls & Directions.NorthWest) == Directions.NorthWest)
                AddCornerMesh(west, north, center, northWest, corner, WallType.Both);
            else
                AddCornerMesh(west, north, center, northWest, corner, WallType.Floor);
        }
    }

    void AddStraightCell(
        Vector3 west,
        Vector3 northWest,
        Vector3 north, 
        Vector3 northEast,
        Vector3 east,
        Vector3 center, 
        CornerType corner, 
        Directions edges, 
        Directions walls
        )
    {
        switch (walls)
        {
            case Directions.North:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Both, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Both, false, true);
                break;
            case Directions.NorthWest:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, corner, WallType.Both, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Floor, false, true);
                break;
            case Directions.NorthEast:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Floor, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, corner, WallType.Both, false, true);
                break;
            default:
                if ((edges & Directions.NorthWest) != Directions.NorthWest)
                    AddCornerMesh(west, north, center, northWest, CornerType.Square, WallType.Floor, true, false);
                if ((edges & Directions.NorthEast) != Directions.NorthEast)
                    AddCornerMesh(north, east, center, northEast, CornerType.Square, WallType.Floor, false, true);
                break;
        }
    }

    void AddInnerCornerCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (edges)
        {
            //case Directions.East:
            //    AddStraightCell(south, west, north, center, (walls == Directions.North) ? CornerType.Square : corner, RotateCW(edges), RotateCW(walls) & Directions.North);
            //    break;
            //case Directions.South:
            //    AddStraightCell(west, north, east, center, (walls == Directions.West) ? CornerType.Square : corner, edges, walls & Directions.North);
            //    break;
            case Directions.North:
                switch (walls)
                {
                    case Directions.SouthWest:
                    case Directions.SouthWest | Directions.NorthEast:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddStraight(east, west, WallType.Floor);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, west, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.West:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, west, center, CornerType.Square, WallType.Both);
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.Floor);
                        }
                        break;
                    case Directions.West | Directions.North:
                        AddCorner(south, east, center, corner, WallType.Both);
                        AddStraight(east, west, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.North:
                    default:
                        AddCorner(south, east, center, corner, WallType.Floor);
                        AddStraight(east, west, WallType.Floor);
                        break;
                }
                break;
            case Directions.West:
                switch (walls)
                {
                    case Directions.NorthEast:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddStraight(north, south, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(north, east, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.North:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, south, center, CornerType.Square, WallType.None);
                            AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        }
                        break;
                    case Directions.North | Directions.West:
                        AddStraight(north, south, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    case Directions.SouthWest:
                    case Directions.West:
                    default:
                        AddStraight(north, south, WallType.Floor);
                        AddCorner(south, east, center, corner, WallType.Floor);
                        break;
                }
                break;
            case Directions.North | Directions.West:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                        if (corner != CornerType.Square)
                        {
                            AddCorner(south, east, center, corner, WallType.Both);
                            AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.SouthWest:
                    case Directions.North:
                    case Directions.West:
                    case Directions.NorthEast | Directions.SouthWest:
                    default:
                        if (corner != CornerType.Square)
                        {
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                }
                break;
            case Directions.North | Directions.East:
                switch (walls)
                {
                    case Directions.West:
                    case Directions.North | Directions.West:
                        AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.SouthWest:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                            AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        else
                        {
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.NorthEast:
                    case Directions.North:
                    default:
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                    case Directions.North:
                    case Directions.West:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthWest:
                        if (corner == CornerType.Square)
                            AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        else
                        {
                            AddCorner(west, north, center, corner, WallType.Wall);
                            AddCorner(north, west, center, corner, WallType.None);
                            AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthEast:
                    case Directions.SouthWest:
                    case Directions.NorthEast | Directions.SouthWest:
                    default:
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.West | Directions.South:
                switch (walls)
                {
                    case Directions.North | Directions.West:
                    case Directions.North:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthEast:
                    case Directions.NorthEast | Directions.SouthWest:
                        if (corner == CornerType.Square)
                            AddCorner(north, east, center, corner, WallType.Both);
                        else
                        {
                            AddCorner(north, east, center, corner, WallType.Both);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        }
                        break;
                    case Directions.NorthWest:
                    case Directions.SouthWest:
                    case Directions.West:
                    default:
                        AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            default:
                {
                    switch (walls)
                    {
                        case Directions.NorthWest:
                            AddCorner(west, north, center, corner, WallType.Wall);
                            AddCorner(north, west, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.NorthEast:
                            AddCorner(north, east, center, corner, WallType.Wall);
                            AddCorner(east, north, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.SouthWest:
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.North:
                            AddStraight(west, east, WallType.Wall);
                            AddStraight(east, west, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.West:
                            AddStraight(south, north, WallType.Wall);
                            AddStraight(north, south, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                        case Directions.NorthEast | Directions.SouthWest:
                            AddCorner(north, east, center, corner, WallType.Wall);
                            AddCorner(south, west, center, corner, WallType.Wall);
                            AddCorner(west, south, center, corner, WallType.None);
                            AddCorner(south, east, center, corner, WallType.Floor);
                            AddCorner(east, north, center, corner, WallType.None);
                            break;
                        case Directions.North | Directions.West:
                            AddCorner(south, east, center, corner, WallType.Both);
                            break;
                        default:
                            AddCorner(south, east, center, corner, WallType.Floor);
                            break;
                    }
                }
                break;
        }
    }

    void AddSaddleCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        if (OpenedDiagonals)
            corner = CornerType.Diamond;
        switch (edges)
        {
            case Directions.East | Directions.North:
            case Directions.West | Directions.South:
                break;
            case Directions.North:
            case Directions.West:
            case Directions.North | Directions.West:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        break;
                    default:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        break;
                }
                break;
            case Directions.East:
            case Directions.South:
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    default:
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                }
                break;
            default:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    case Directions.NorthWest:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        AddCorner(west, north, center, corner, WallType.Both);
                        break;
                    case Directions.SouthEast:
                        AddCorner(east, south, center, corner, WallType.Both);
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                    default:
                        AddCorner(east, south, center, corner, WallType.Floor);
                        AddCorner(west, north, center, corner, WallType.Floor);
                        break;
                }
                break;
        }
    }

    void AddCenterCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (walls)
        {
            case Directions.None:
            case Directions.NorthWest:
            case Directions.North:
            case Directions.North | Directions.SouthWest:
            case Directions.NorthWest | Directions.SouthEast:
            case Directions.All:
                AddRotatedCenterCell(north, east, south, west, center, corner, edges, walls);
                break;
            case Directions.NorthEast:
            case Directions.East:
            case Directions.North | Directions.SouthEast:
            case Directions.NorthEast | Directions.SouthWest:
                AddRotatedCenterCell(east, south, west, north, center, corner, RotateCCW(edges), RotateCCW(walls));
                break;
            case Directions.SouthEast:
            case Directions.South:
            case Directions.NorthEast | Directions.South:
                AddRotatedCenterCell(south, west, north, east, center, corner, Rotate180(edges), Rotate180(walls));
                break;
            case Directions.SouthWest:
            case Directions.West:
            case Directions.West | Directions.SouthEast:
                AddRotatedCenterCell(west, north, east, south, center, corner, RotateCW(edges), RotateCW(walls));
                break;
        }
    }

    void AddRotatedCenterCell(Vector3 north, Vector3 east, Vector3 south, Vector3 west, Vector3 center, CornerType corner, Directions edges, Directions walls)
    {
        switch (edges)
        {
            case Directions.North:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(south, west, center, corner, WallType.Both);
                            AddCorner(east, south, center, corner, WallType.Floor);
                        }
                        else
                        {
                            AddCorner(south, east, center, corner, WallType.Wall);
                            AddStraight(east, west, WallType.Both);
                            AddCorner(east, south, center, corner, WallType.None);
                        }
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddStraight(east, west, WallType.Floor);
                        break;
                    case Directions.All:
                        AddStraight(east, west, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    default:
                        AddStraight(east, west, WallType.Floor);
                        break;
                }
                break;
            case Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddStraight(west, east, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddStraight(west, east, WallType.Floor);
                        break;
                    case Directions.West | Directions.North:
                    case Directions.North:
                    case Directions.All:
                        AddStraight(west, east, WallType.Both);
                        break;
                    default:
                        AddStraight(west, east, WallType.Floor);
                        break;
                }
                break;
            case Directions.East:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddStraight(south, north, WallType.Floor);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.North:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddStraight(south, north, WallType.Floor);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddStraight(south, north, WallType.Both);
                        break;
                    default:
                        AddStraight(south, north, WallType.Floor);
                        break;
                }
                break;
            case Directions.West:
                switch (walls)
                {
                    case Directions.North:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.West | Directions.North:
                        AddStraight(north, south, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.Wall);
                        AddCorner(east, south, center, corner, WallType.None);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;
                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddStraight(north, south, WallType.Floor);
                        break;
                    case Directions.All:
                        AddStraight(north, south, WallType.Both);
                        break;
                    case Directions.NorthWest:
                    default:
                        AddStraight(north, south, WallType.Floor);
                        break;
                }
                break;
            case Directions.West | Directions.North:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                        AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        AddCorner(south, east, center, corner, WallType.None);
                        AddCorner(east, south, center, corner, WallType.Floor);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (corner == CornerType.Square)
                        {
                            AddCorner(east, south, center, corner, WallType.Wall);
                        }
                        else
                        {
                            AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                            AddCorner(south, east, center, corner, WallType.None);
                            AddCorner(east, south, center, corner, WallType.Wall);
                        }
                        break;
                    case Directions.All:
                        AddCorner(east, south, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    default:
                        AddCorner(east, south, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.North | Directions.East:
                switch (walls)
                {
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(south, west, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.North:
                    case Directions.NorthWest:
                    case Directions.NorthWest | Directions.SouthEast:
                    default:
                        AddCorner(south, west, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.East | Directions.South:
                switch (walls)
                {
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                    case Directions.North:
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(west, north, center, CornerType.Square, WallType.Both);
                        break;
                    default:
                        AddCorner(west, north, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            case Directions.South | Directions.West:
                switch (walls)
                {
                    case Directions.North:
                    case Directions.West | Directions.North:
                    case Directions.All:
                        AddCorner(north, east, center, CornerType.Square, WallType.Both);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                    case Directions.NorthWest:
                    default:
                        AddCorner(north, east, center, CornerType.Square, WallType.Floor);
                        break;
                }
                break;
            default:
                switch (walls)
                {
                    case Directions.NorthWest:
                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);
                        break;
                    case Directions.North:
                        AddStraight(west, east, WallType.Wall);
                        AddStraight(east, west, WallType.None);
                        break;
                    case Directions.West | Directions.North:
                        AddCorner(south, east, center, corner, WallType.Wall);
                        AddCorner(east, south, center, corner, WallType.None);
                        break;
                    case Directions.NorthWest | Directions.SouthEast:
                        if (OpenedDiagonals)
                            corner = CornerType.Diamond;

                        AddCorner(west, north, center, corner, WallType.Wall);
                        AddCorner(north, west, center, corner, WallType.None);

                        AddCorner(east, south, center, corner, WallType.Wall);
                        AddCorner(south, east, center, corner, WallType.None);
                        break;
                    default:
                        break;
                }
                break;
        }
    }

    private Vector3[] GetCornerPoints(Vector3 start, Vector3 end, Vector3 center, CornerType type)
    {
        switch (type)
        {
            case CornerType.Square:
                return new Vector3[]
                {
                    start,
                    center,
                    end
                };
            case CornerType.Rounded:
                return new Vector3[] 
                {
                    start,
                    (start + center) / 2,
                    (end + center) / 2,
                    end
                };
            default:
                return new Vector3[]
                {
                    start,
                    end
                };
        }
    }

    private void AddCornerMesh(Vector3 start, Vector3 end, Vector3 center, Vector3 corner, CornerType type, WallType wallType, bool startWalls = true, bool endWalls = true)
    {
        switch (type)
        {
            case CornerType.Diamond:
                break;
            case CornerType.Square:
{'fraction_non_alphanumeric': 0.07776174442841109, 'fraction_numerical': 0.0028287409239790193, 'mean_word_length': 1.1992494458996574, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '247712', 'n_tokens_mistral': 22866, 'n_tokens_neox': 20391, 'n_words': 4689}