internal List <Vector3> Get_sensitive_vertices(GameObject rmc)
    {
        List <Vector3> sensitive_vertices = new List <Vector3>();

        // suppose MeshVerts returns nxm unconstrained mesh
        Vector3[] verts         = Build.GetMeshVerts(rmc);
        Quarter[] tile_quarters = Quarter.Generate_All_Quarters(rmc);

        for (int index = 0; index < verts.Length; index++)
        {
            Vector3 v = rmc.transform.TransformPoint(verts[index]);
            v.x = Mathf.Round(v.x);
            v.z = Mathf.Round(v.z);

            if (!Consts.IsWithinMapBounds(v))
            {
                continue;
            }
            if (v.x % 4 == 0 && v.z % 4 == 0)
            {
                sensitive_vertices.Add(v);
                continue;
            }

            // find a quarter that given vertex belongs to and get information about restriction pattern
            Quarter quarter = tile_quarters.Aggregate(
                (minItem, nextItem) => Consts.Distance(minItem.pos, v) < Consts.Distance(nextItem.pos, v) ? minItem : nextItem);


            if (quarter.qt.Unrestricted())
            {
                sensitive_vertices.Add(v);
            }
            else if (quarter.qt.Both_restricted())
            {
                if (quarter.original_grid.Contains(Consts.PosToIndex(v)) && !Consts.Lies_on_any_restricted_borders(v, quarter))
                {
                    sensitive_vertices.Add(v);
                }
            }
            else if (quarter.qt.Horizontal_restricted())
            {
                if (quarter.original_grid.Contains(Consts.PosToIndex(v)) && !Consts.Lies_on_restricted_border(v, BorderType.Horizontal, quarter))
                {
                    sensitive_vertices.Add(v);
                }
            }
            else if (quarter.qt.Vertical_restricted())
            {
                if (quarter.original_grid.Contains(Consts.PosToIndex(v)) && !Consts.Lies_on_restricted_border(v, BorderType.Vertical, quarter))
                {
                    sensitive_vertices.Add(v);
                }
            }
        }
        return(sensitive_vertices);
    }
    private void FreeElevation()
    {
        // Highlight.pos is center vertex
        if (Highlight.pos.x != InitialPos.x || Highlight.pos.z != InitialPos.z)
        {         // vertex -> vertex
            InitialPos = Highlight.pos;
            TargetSetHeights.Clear();
        }
        HashSet <int> indexes = new HashSet <int>();

        for (float z = Highlight.pos.z - RadiusSlider.value; z <= Highlight.pos.z + RadiusSlider.value; z++)
        {
            for (float x = Highlight.pos.x - RadiusSlider.value; x <= Highlight.pos.x + RadiusSlider.value; x++)
            {
                if (Consts.IsWithinMapBounds(x, z))
                {
                    int     idx        = Consts.PosToIndex((int)x, (int)z);
                    Vector3 currentpos = Consts.IndexToPos(idx);
                    float   dist       = Consts.Distance(currentpos, Highlight.pos);
                    if (Mathf.Round(dist) > RadiusSlider.value - 1)
                    {
                        continue;
                    }

                    float TargetSetHeight;
                    if (TargetSetHeights.ContainsKey(idx))
                    {
                        TargetSetHeight = TargetSetHeights[idx];
                    }
                    else
                    {
                        float Hdiff = Consts.SliderValue2RealHeight(HeightSlider.value) - currentpos.y;
                        TargetSetHeight = currentpos.y + Hdiff
                                          * Consts.Smoothstep(0, 1, (RadiusSlider.value - dist) / RadiusSlider.value);
                        TargetSetHeights.Add(idx, TargetSetHeight);
                    }
                    Consts.former_heights[idx] += (TargetSetHeight - currentpos.y)
                                                  * Mathf.Pow(IntensitySlider.value * 5, 2) / 10000;
                    Consts.current_heights[idx] = Consts.former_heights[idx];
                    UndoBuffer.AddVertexPair(currentpos, Consts.IndexToPos(idx));
                    indexes.Add(idx);
                }
            }
        }
        Consts.UpdateMapColliders(indexes);
        //Search for any tiles
        RaycastHit[] hits = Physics.BoxCastAll(new Vector3(Highlight.pos.x, Consts.MAX_H, Highlight.pos.z),
                                               new Vector3(RadiusSlider.value + .5f, 1, RadiusSlider.value + .5f),
                                               Vector3.down, Quaternion.identity, Consts.RAY_H, 1 << 9);
        List <GameObject> hitsList = hits.Select(hit => hit.transform.gameObject).ToList();

        Build.UpdateTiles(hitsList);
    }
    private void Smoothing()
    {
        HashSet <int> indexes = new HashSet <int>();

        for (float z = Highlight.pos.z - RadiusSlider.value; z <= Highlight.pos.z + RadiusSlider.value; z++)
        {
            for (float x = Highlight.pos.x - RadiusSlider.value; x <= Highlight.pos.x + RadiusSlider.value; x++)
            {
                if (Consts.IsWithinMapBounds(x, z))
                {
                    int     idx        = Consts.PosToIndex((int)x, (int)z);
                    Vector3 currentpos = Consts.IndexToPos(idx);
                    float   dist       = Consts.Distance(currentpos, Highlight.pos);
                    if (Mathf.Round(dist) > RadiusSlider.value - 1)
                    {
                        continue;
                    }

                    float height_sum = 0;
                    for (float xx = currentpos.x - 1; xx <= currentpos.x + 1; xx++)
                    {
                        for (float zz = currentpos.z - 1; zz <= currentpos.z + 1; zz++)
                        {
                            if (xx == currentpos.x && zz == currentpos.z)
                            {
                                continue;
                            }
                            Vector3 neighbor_pos = new Vector3(xx, 0, zz);
                            height_sum += Consts.current_heights[Consts.PosToIndex(neighbor_pos)];
                        }
                    }
                    float avg = height_sum / 8f;

                    Vector3 for_buffer = Consts.IndexToPos(idx);
                    Consts.current_heights[idx] += (avg - Consts.current_heights[idx])
                                                   * Mathf.Pow(IntensitySlider.value * 5, 2) / 10000;
                    Consts.former_heights[idx] = Consts.current_heights[idx];
                    UndoBuffer.AddVertexPair(for_buffer, Consts.IndexToPos(idx));
                    indexes.Add(idx);
                }
            }
        }
        Consts.UpdateMapColliders(indexes);
        //Search for any tiles
        RaycastHit[] hits = Physics.BoxCastAll(new Vector3(Highlight.pos.x, Consts.MAX_H, Highlight.pos.z),
                                               new Vector3(RadiusSlider.value + 1, 1, RadiusSlider.value),
                                               Vector3.down, Quaternion.identity, Consts.RAY_H, 1 << 9);
        List <GameObject> hitsList = hits.Select(hit => hit.transform.gameObject).ToList();

        Build.UpdateTiles(hitsList);
    }
    private void Amplification(int dir = 1)
    {
        // Highlight.pos is center vertex
        HashSet <int> indexes = new HashSet <int>();

        for (float z = Highlight.pos.z - RadiusSlider.value; z <= Highlight.pos.z + RadiusSlider.value; z++)
        {
            for (float x = Highlight.pos.x - RadiusSlider.value; x <= Highlight.pos.x + RadiusSlider.value; x++)
            {
                if (Consts.IsWithinMapBounds(x, z))
                {
                    int   idx        = Consts.PosToIndex((int)x, (int)z);
                    float heightdiff = Consts.current_heights[idx] - Consts.SliderValue2RealHeight(HeightSlider.value);
                    float dist       = Consts.Distance(Consts.IndexToPos(idx), Highlight.pos);
                    if (Mathf.Round(dist) > RadiusSlider.value - 1)
                    {
                        continue;
                    }
                    Vector3 for_buffer = Consts.IndexToPos(idx);
                    Consts.former_heights[idx] += dir * heightdiff * Mathf.Pow(IntensitySlider.value * 2, 2) / 20000
                                                  * Consts.Smoothstep(0, 1, (RadiusSlider.value - dist) / RadiusSlider.value);
                    Consts.current_heights[idx] = Consts.former_heights[idx];
                    UndoBuffer.AddVertexPair(for_buffer, Consts.IndexToPos(idx));
                    indexes.Add(idx);
                }
            }
        }
        Consts.UpdateMapColliders(indexes);
        //Search for any tiles
        RaycastHit[] hits = Physics.BoxCastAll(new Vector3(Highlight.pos.x, Consts.MAX_H, Highlight.pos.z),
                                               new Vector3(RadiusSlider.value + 1, 1, RadiusSlider.value),
                                               Vector3.down, Quaternion.identity, Consts.RAY_H, 1 << 9);
        List <GameObject> hitsList = hits.Select(hit => hit.transform.gameObject).ToList();

        Build.UpdateTiles(hitsList);
    }
    /// <summary>
    /// You need to add borders to vault before calling this
    /// </summary>
    static bool Calculate_All_RMC_points(GameObject rmc)
    {
        // suppose MeshVerts returns nxm unconstrained mesh
        Vector3[] verts = GetMeshVerts(rmc);
        Vector3   pos   = Vpos2tpos(rmc);

        Quarter[] tile_quarters = Quarter.Generate_Quarters(rmc);

        for (int index = 0; index < verts.Length; index++)
        {
            Vector3Int v = Vector3Int.RoundToInt(rmc.transform.TransformPoint(verts[index]));

            if (!Consts.IsWithinMapBounds(v))
            {
                continue;
            }
            // find a quarter that given vertex belongs to and get information about restriction pattern
            Quarter quarter = tile_quarters.Aggregate(
                (minItem, nextItem) => Consts.Distance(minItem.pos, v) < Consts.Distance(nextItem.pos, v) ? minItem : nextItem);

            if (quarter.qt.Unrestricted())
            {
                verts[index].y = Consts.current_heights[Consts.PosToIndex(v)];
            }
            else if (quarter.qt.Both_restricted())
            {
                if (Consts.Lies_on_restricted_border(v, BorderType.Horizontal, quarter))
                {
                    verts[index].y = Calculate_horizontal_height(v);
                }
                else if (Consts.Lies_on_restricted_border(v, BorderType.Vertical, quarter))
                {
                    verts[index].y = Calculate_vertical_height(v);
                }
                else if (quarter.original_grid.Contains(Consts.PosToIndex(v)))
                {
                    verts[index].y = Consts.current_heights[Consts.PosToIndex(v)];
                }
                else
                {
                    if (quarter.original_grid.Contains(Consts.PosToIndex(new Vector3(v.x / 4 * 4, v.y, v.z))))
                    {
                        verts[index].y = Calculate_horizontal_height(v);
                    }
                    else if (quarter.original_grid.Contains(Consts.PosToIndex(new Vector3(v.x, v.y, v.z / 4 * 4))))
                    {
                        verts[index].y = Calculate_vertical_height(v);
                    }
                    else
                    {
                        verts[index].y = Razor_both_restricted_formula(v);
                    }
                }
            }
            else if (quarter.qt.Horizontal_restricted())
            {
                if (!quarter.original_grid.Contains(Consts.PosToIndex(v)) || Consts.Lies_on_restricted_border(v, BorderType.Horizontal, quarter))
                {
                    verts[index].y = Calculate_horizontal_height(v);
                }
                else
                {
                    verts[index].y = Consts.current_heights[Consts.PosToIndex(v)];
                }
            }
            else if (quarter.qt.Vertical_restricted())
            {
                if (!quarter.original_grid.Contains(Consts.PosToIndex(v)) || Consts.Lies_on_restricted_border(v, BorderType.Vertical, quarter))
                {
                    verts[index].y = Calculate_vertical_height(v);
                }
                else
                {
                    verts[index].y = Consts.current_heights[Consts.PosToIndex(v)];
                }
            }
            Consts.current_heights[Consts.PosToIndex(v)] = verts[index].y;

            if (float.IsNaN(verts[index].y) || float.IsInfinity(verts[index].y))
            {
                return(false);
            }
        }
        UpdateMeshes(rmc, verts);
        return(true);
    }
    private void RectangularElevation()
    {
        if (P1.x == -1)
        {
            // Get initial position and set znacznik there
            P1        = Highlight.pos;
            P1_marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
            P1_marker.transform.localScale = new Vector3(.25f, 1, .25f);
            P1_marker.transform.position   = Highlight.pos;
        }
        else
        {
            // Time to get second position
            Vector3       P2      = Highlight.pos;
            Vector3       BL      = new Vector3(Mathf.Min(P1.x, P2.x), 0, Mathf.Min(P1.z, P2.z));
            Vector3       TR      = new Vector3(Mathf.Max(P1.x, P2.x), 0, Mathf.Max(P1.z, P2.z));
            HashSet <int> indexes = new HashSet <int>();
            for (float z = BL.z - (int)RadiusSlider.value; z <= TR.z + RadiusSlider.value; z++)
            {
                for (float x = BL.x - (int)RadiusSlider.value; x <= TR.x + RadiusSlider.value; x++)
                {
                    if (Consts.IsWithinMapBounds(x, z))
                    {
                        int     idx        = Consts.PosToIndex((int)x, (int)z);
                        Vector3 currentpos = Consts.IndexToPos(idx);
                        if (x >= BL.x && x <= TR.x && z >= BL.z && z <= TR.z)
                        {
                            Consts.former_heights[idx] = Consts.SliderValue2RealHeight(HeightSlider.value);
                        }
                        else
                        {
                            Vector3 Closest = GetClosestEdgeVertex(currentpos, BL, TR);
                            float   dist    = Consts.Distance(currentpos, Closest);
                            if (Mathf.Round(dist) > RadiusSlider.value - 1)
                            {
                                continue;
                            }
                            float Hdiff = Consts.SliderValue2RealHeight(HeightSlider.value) - Consts.current_heights[idx];
                            Consts.former_heights[idx] += Hdiff * Consts.Smoothstep(0, 1, (RadiusSlider.value - dist) / RadiusSlider.value);
                        }
                        Consts.current_heights[idx] = Consts.former_heights[idx];
                        UndoBuffer.AddVertexPair(currentpos, Consts.IndexToPos(idx));
                        indexes.Add(idx);
                    }
                }
            }
            Consts.UpdateMapColliders(indexes);
            Vector3 center = new Vector3(0.5f * (P1.x + P2.x), Consts.MAX_H, 0.5f * (P1.z + P2.z));
            Vector3 bounds = new Vector3(0.5f * Mathf.Abs(P1.x - P2.x) + RadiusSlider.value + .5f, 1f,
                                         0.5f * Mathf.Abs(P1.z - P2.z) + RadiusSlider.value + .5f);
            RaycastHit[] hits = Physics.BoxCastAll(center, bounds, Vector3.down, Quaternion.identity, Consts.RAY_H, 1 << 9);
            Build.UpdateTiles(hits.Select(hit => hit.transform.gameObject).ToList());
            UndoBuffer.ApplyTerrainOperation();
            RemoveIndicator();
        }

        Vector3 GetClosestEdgeVertex(Vector3 v, Vector3 LD, Vector3 PG)
        {
            Vector3 result = new Vector3();

            if (v.x < LD.x)
            {
                result.x = LD.x;
            }
            else if (v.x > PG.x)
            {
                result.x = PG.x;
            }
            else
            {
                result.x = v.x;
            }

            if (v.z < LD.z)
            {
                result.z = LD.z;
            }
            else if (v.z > PG.z)
            {
                result.z = PG.z;
            }
            else
            {
                result.z = v.z;
            }
            return(result);
        }
    }