/// <summary>
    /// Update the visuals for a cell.
    /// </summary>
    /// <param name="cell">Cell.</param>
    private static void _UpdateVisuals(QualityCell cell)
    {
        bool anyAreTrue  = false;
        bool anyAreFalse = false;

        for (int it = 0; it < cell.m_angleVisited.Length; ++it)
        {
            // The first object is the "fully filled" object.  Children are after that.
            GameObject childObject = cell.m_visuals.transform.GetChild(it + 1).gameObject;
            bool       value       = cell.m_angleVisited[it];

            childObject.SetActive(value);
            if (value)
            {
                anyAreTrue = true;
            }
            else
            {
                anyAreFalse = true;
            }
        }

        cell.m_visuals.SetActive(anyAreTrue);
        cell.m_visuals.transform.GetChild(0).gameObject.SetActive(anyAreTrue && !anyAreFalse);
    }
    /// <summary>
    /// LateUpdate is called after all Update functions have been called.
    /// </summary>
    public void LateUpdate()
    {
        // This logic all needs to run after the pose controller has moved in its update, hence LateUpdate.
        int cellX;
        int cellZ;
        int angleIndex;

        if (_DiscretizeTransform(m_poseController, out cellX, out cellZ, out angleIndex))
        {
            m_badQualityTransformTime = 0;

            QualityCell cell = _InternCell(cellX, cellZ);
            cell.m_angleVisited[angleIndex] = true;
            _UpdateVisuals(cell);
        }
        else
        {
            m_badQualityTransformTime += Time.deltaTime;
        }

        // Hide / show the bad quality UI as needed.
        if (m_badQualityTransformTime > MAX_ALLOWED_BAD_QUALITY_TRANSFORM_TIME)
        {
            m_badQualityTransformParent.gameObject.SetActive(true);
        }
        else
        {
            m_badQualityTransformParent.gameObject.SetActive(false);
        }

        // Update the quality text
        m_qualityText.text = string.Format("{0}% coverage", Mathf.FloorToInt(_CalculateQuality() * 100));
    }
    /// <summary>
    /// Get the cell for a specific coordinate, allocating new cells if necessary.
    /// 
    /// Think string.Intern.
    /// </summary>
    /// <returns>The cell for that coordinate.</returns>
    /// <param name="x">X index for the cell.</param>
    /// <param name="z">Z index for the cell.</param>
    private QualityCell _InternCell(int x, int z)
    {
        int curSizeX = m_cellQualities.Count;
        int curSizeY = 0;
        if (curSizeX > 0)
        {
            curSizeY = m_cellQualities[0].Count;
        }

        // Ensure that x, y can be used to index into m_cellQualities, growing the array if needed.
        while (x < 0)
        {
            List<QualityCell> toInsert = new List<QualityCell>();
            for (int it = 0; it < curSizeY; ++it)
            {
                QualityCell cell = new QualityCell();
                toInsert.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x - CELL_SIZE, 0, m_cellsOrigin.y + (it * CELL_SIZE));
                _UpdateVisuals(cell);
            }

            m_cellQualities.Insert(0, toInsert);

            ++curSizeX;
            ++x;
            m_cellsOrigin.x -= CELL_SIZE;
        }

        while (x >= curSizeX)
        {
            List<QualityCell> toInsert = new List<QualityCell>();
            for (int it = 0; it < curSizeY; ++it)
            {
                QualityCell cell = new QualityCell();
                toInsert.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (curSizeX * CELL_SIZE), 0, m_cellsOrigin.y + (it * CELL_SIZE));
                _UpdateVisuals(cell);
            }

            m_cellQualities.Add(toInsert);
            
            ++curSizeX;
        }

        while (z < 0)
        {
            for (int it = 0; it < curSizeX; ++it)
            {
                List<QualityCell> cellList = m_cellQualities[it];
                QualityCell cell = new QualityCell();
                cellList.Insert(0, cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (it * CELL_SIZE), 0, m_cellsOrigin.y - CELL_SIZE);
                _UpdateVisuals(cell);
            }

            ++curSizeY;
            ++z;
            m_cellsOrigin.y -= CELL_SIZE;
        }

        while (z >= curSizeY)
        {
            for (int it = 0; it < curSizeX; ++it)
            {
                List<QualityCell> cellList = m_cellQualities[it];
                QualityCell cell = new QualityCell();
                cellList.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (it * CELL_SIZE), 0, m_cellsOrigin.y + (curSizeY * CELL_SIZE));
                _UpdateVisuals(cell);
            }

            ++curSizeY;
        }

        // Now the indexes are both in-bounds of the cell grid.
        return m_cellQualities[x][z];
    }
    /// <summary>
    /// Update the visuals for a cell.
    /// </summary>
    /// <param name="cell">Cell to update.</param>
    private static void _UpdateVisuals(QualityCell cell)
    {
        bool anyAreTrue = false;
        bool anyAreFalse = false;

        for (int it = 0; it < cell.m_angleVisited.Length; ++it)
        {
            // The first object is the "fully filled" object.  Children are after that.
            GameObject childObject = cell.m_visuals.transform.GetChild(it + 1).gameObject;
            bool value = cell.m_angleVisited[it];
            
            childObject.SetActive(value);
            if (value)
            {
                anyAreTrue = true;
            }
            else
            {
                anyAreFalse = true;
            }
        }

        cell.m_visuals.SetActive(anyAreTrue);
        cell.m_visuals.transform.GetChild(0).gameObject.SetActive(anyAreTrue && !anyAreFalse);
    }
    /// <summary>
    /// Get the cell for a specific coordinate, allocating new cells if necessary.
    ///
    /// Think string.Intern.
    /// </summary>
    /// <returns>The cell for that coordinate.</returns>
    /// <param name="x">X index for the cell.</param>
    /// <param name="z">Z index for the cell.</param>
    private QualityCell _InternCell(int x, int z)
    {
        int curSizeX = m_cellQualities.Count;
        int curSizeY = 0;

        if (curSizeX > 0)
        {
            curSizeY = m_cellQualities[0].Count;
        }

        // Ensure that x, y can be used to index into m_cellQualities, growing the array if needed.
        while (x < 0)
        {
            List <QualityCell> toInsert = new List <QualityCell>();
            for (int it = 0; it < curSizeY; ++it)
            {
                QualityCell cell = new QualityCell();
                toInsert.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x - CELL_SIZE, 0, m_cellsOrigin.y + (it * CELL_SIZE));
                _UpdateVisuals(cell);
            }
            m_cellQualities.Insert(0, toInsert);

            ++curSizeX;
            ++x;
            m_cellsOrigin.x -= CELL_SIZE;
        }
        while (x >= curSizeX)
        {
            List <QualityCell> toInsert = new List <QualityCell>();
            for (int it = 0; it < curSizeY; ++it)
            {
                QualityCell cell = new QualityCell();
                toInsert.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (curSizeX * CELL_SIZE), 0, m_cellsOrigin.y + (it * CELL_SIZE));
                _UpdateVisuals(cell);
            }
            m_cellQualities.Add(toInsert);

            ++curSizeX;
        }

        while (z < 0)
        {
            for (int it = 0; it < curSizeX; ++it)
            {
                List <QualityCell> cellList = m_cellQualities[it];
                QualityCell        cell     = new QualityCell();
                cellList.Insert(0, cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (it * CELL_SIZE), 0, m_cellsOrigin.y - CELL_SIZE);
                _UpdateVisuals(cell);
            }

            ++curSizeY;
            ++z;
            m_cellsOrigin.y -= CELL_SIZE;
        }
        while (z >= curSizeY)
        {
            for (int it = 0; it < curSizeX; ++it)
            {
                List <QualityCell> cellList = m_cellQualities[it];
                QualityCell        cell     = new QualityCell();
                cellList.Add(cell);

                cell.m_visuals = Instantiate(m_cellVisualsPrefab) as GameObject;
                cell.m_visuals.transform.position = new Vector3(
                    m_cellsOrigin.x + (it * CELL_SIZE), 0, m_cellsOrigin.y + (curSizeY * CELL_SIZE));
                _UpdateVisuals(cell);
            }

            ++curSizeY;
        }

        // Now the indexes are both in-bounds of the cell grid.
        return(m_cellQualities[x][z]);
    }