/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using Leap.Unity.Infix; using UnityEngine; namespace Leap.Unity.Geometry { using UnityRect = UnityEngine.Rect; public enum VerticalOrigin { Top, Bottom } public struct LeapGrid { public LocalRect rect; public int numRows; public int numCols; public Margins cellMargins; public VerticalOrigin verticalOrigin; public bool rowMajor; public LeapGrid(UnityRect rect, int numRows = 1, int numCols = 1, Margins? cellMargins = null, VerticalOrigin? verticalOrigin = null, bool rowMajor = false) : this(new LocalRect(rect), numRows, numCols, cellMargins, verticalOrigin, rowMajor) { } public LeapGrid(LocalRect rect, int numRows = 1, int numCols = 1, Margins? cellMargins = null, VerticalOrigin? verticalOrigin = null, bool rowMajor = false) { var useMargins = cellMargins.UnwrapOr(Margins.All(0f)); var useVertOrigin = verticalOrigin.UnwrapOr(VerticalOrigin.Bottom); this.rect = rect; this.numRows = numRows; this.numCols = numCols; this.cellMargins = useMargins; this.verticalOrigin = useVertOrigin; this.rowMajor = rowMajor; } public int numCells { get { return numRows * numCols; } } public Cell this[int idx] { get { var cellWidth = rect.radii.x * 2f / numCols; var cellHeight = rect.radii.y * 2f / numRows; int row = idx / numCols; int col = idx % numCols; if (rowMajor) { row = idx % numRows; col = idx / numRows; idx = row * numCols + col; } var origin = verticalOrigin == VerticalOrigin.Bottom ? rect.corner00 : rect.corner10; var centerFromOrigin = new Vector3( col * cellWidth + (cellWidth / 2f), row * cellHeight + (cellHeight / 2f), 0f ).CompMul(verticalOrigin == VerticalOrigin.Top ? new Vector3(1f, -1f, 1f) : Vector3.one); return new Cell() { row = row, col = col, index = idx, margins = cellMargins, outerRect = new LocalRect() { center = origin + centerFromOrigin, radii = new Vector2(cellWidth / 2f, cellHeight / 2f) } }; } } public Cell GetMerged(int idx0, int idx1) { if (idx0 == idx1) { return this[idx0]; } int row0 = getRow(idx0), row1 = getRow(idx1); if (row0 > row1) { Utils.Swap(ref row0, ref row1); } var rowStart = getRow(idx0); var numMergedRows = 1 + (row1 - row0); int col0 = getCol(idx0), col1 = getCol(idx1); if (col0 > col1) { Utils.Swap(ref col0, ref col1); } var colStart = getCol(idx0); var numMergedCols = 1 + (col1 - col0); var cellWidth = rect.radii.x * 2f / this.numCols; var cellHeight = rect.radii.y * 2f / this.numRows; var mergedCellWidth = cellWidth * numMergedCols; var mergedCellHeight = cellHeight * numMergedRows; var origin = verticalOrigin == VerticalOrigin.Bottom ? rect.corner00 : rect.corner10; var centerFromOrigin = new Vector3( colStart * cellWidth + (mergedCellWidth / 2f), rowStart * cellHeight + (mergedCellHeight / 2f), 0f ).CompMul(verticalOrigin == VerticalOrigin.Top ? new Vector3(1f, -1f, 1f) : Vector3.one); return new Cell() { row = rowStart, col = colStart, index = (colStart + this.numCols * rowStart), margins = cellMargins, outerRect = new LocalRect() { center = origin + centerFromOrigin, radii = new Vector2(mergedCellWidth / 2f, mergedCellHeight / 2f) } }; } private int getRow(int idx) { return idx / numCols; } private int getCol(int idx) { return idx % numCols; } private int getIndex(int row, int col) { return row * numCols + col; } public struct Cell { public int row; public int col; public int index; public Margins margins; public LocalRect outerRect; public LocalRect innerRect { get { return outerRect.PadInner(margins); } } /// Shorthand for cell.innerRect. public LocalRect rect { get { return innerRect; } } public UnityRect unityRect { get { return rect.ToUnityRect(); } } } public CellEnumerator GetEnumerator() { return new CellEnumerator(this); } /// Returns a CellEnumerator that enumerates cells within /// the rectangular subgrid defined by cell indices at two opposite /// corners of the subgrid. public CellEnumerator EnumerateCells(int subGridBegin, int subGridEnd) { return new CellEnumerator(this, subGridBegin, subGridEnd); } public struct CellEnumerator { public LeapGrid grid; public int idx; public float cellWidth; public float cellHeight; bool _useSubGrid; int _lastIndex; int _col0, _col1; public CellEnumerator(LeapGrid grid) { this.grid = grid; this.idx = -1; this.cellWidth = grid.rect.radii.x * 2f / grid.numCols; this.cellHeight = grid.rect.radii.y * 2f / grid.numRows; _useSubGrid = false; _col0 = 0; _col1 = 0; _lastIndex = grid.numCells - 1; } public CellEnumerator GetEnumerator() { return this; } /// Initializes a CellEnumerator that enumerates cells within /// the rectangular subgrid defined by cell indices at two opposite /// corners of the subgrid. public CellEnumerator(LeapGrid grid, int subGridBegin, int subGridEnd) : this(grid) { int idx0 = subGridBegin, idx1 = subGridEnd; int row0 = grid.getRow(idx0), row1 = grid.getRow(idx1); if (row0 > row1) { Utils.Swap(ref row0, ref row1); } int col0 = grid.getCol(idx0), col1 = grid.getCol(idx1); if (col0 > col1) { Utils.Swap(ref col0, ref col1); } _col0 = col0; _col1 = col1; _lastIndex = Mathf.Max(subGridBegin, subGridEnd); idx = grid.getIndex(row0, col0) - 1; _useSubGrid = true; } public bool MoveNext() { idx += 1; if (_useSubGrid) { //var row = grid.getRow(idx); var col = grid.getCol(idx); if (col < _col0) { idx += _col0 - col; } if (col > _col1) { idx += grid.numCols - col; } if (idx > _lastIndex) { return false; } } if (idx >= grid.numCells) { return false; } else { return true; } } public Cell Current { get { return grid[idx]; } } } } }