< Summary

Class:TacticalPolarGridGraphic
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/UI/TacticalPolarGridGraphic.cs
Covered lines:0
Uncovered lines:142
Coverable lines:142
Total lines:230
Line coverage:0% (0 of 142)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:15
Method coverage:0% (0 of 15)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
TacticalPolarGridGraphic()0%2100%
OnEnable()0%6200%
OnDestroy()0%2100%
CycleRangeUp()0%2100%
CycleRangeDown()0%2100%
OnPopulateMesh(...)0%2100%
DrawRangeRings(...)0%12300%
DrawBearingLines(...)0%6200%
DrawCircle(...)0%6200%
DrawLine(...)0%2100%
ClearRangeTexts()0%12300%
UpdateRangeTexts()0%20400%
UpdateRangeTextsPositions(...)0%20400%
GetRangeLabelText(...)0%6200%

File(s)

/github/workspace/Assets/Scripts/UI/TacticalPolarGridGraphic.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using TMPro;
 3using UnityEngine;
 4using UnityEngine.UI;
 5
 6[RequireComponent(typeof(CanvasRenderer))]
 7public class TacticalPolarGridGraphic : Graphic {
 8  [SerializeField]
 09  private Color _gridColor = new Color(0.0f, 1.0f, 0.0f, 0.3f);
 10
 11  [SerializeField]
 012  private Color _textColor = new Color(0.2f, 0.2f, 0.2f, 0.5f);
 13
 14  [SerializeField]
 015  private int _numberOfBearingLines = 36;  // Every 10 degrees.
 16
 17  [SerializeField]
 018  private int _numberOfRangeRings = 5;
 19
 20  [SerializeField]
 021  private float[] _rangeScales = { 100f, 1000f, 10000f, 40000f };  // meters
 22
 23  [SerializeField]
 024  private float _lineWidth = 2f;
 25
 026  private int _currentRangeIndex = 1;  // Start with 10 km range.
 27
 28  [SerializeField]
 029  private GameObject _rangeText = null!;
 30
 031  private List<TextMeshProUGUI> _rangeTexts = new List<TextMeshProUGUI>();
 32
 033  private float _currentScaleFactor = 1f;
 34
 035  public float CurrentScaleFactor => _currentScaleFactor;
 36
 037  protected override void OnEnable() {
 038    base.OnEnable();
 039    if (Application.isPlaying) {
 040      ClearRangeTexts();
 041      UpdateRangeTexts();
 042    }
 043  }
 44
 045  protected override void OnDestroy() {
 046    base.OnDestroy();
 047    ClearRangeTexts();
 048  }
 49
 050  public void CycleRangeUp() {
 051    _currentRangeIndex = (_currentRangeIndex + 1) % _rangeScales.Length;
 052    SetVerticesDirty();
 053    UpdateRangeTexts();
 054  }
 55
 056  public void CycleRangeDown() {
 057    _currentRangeIndex = (_currentRangeIndex - 1 + _rangeScales.Length) % _rangeScales.Length;
 058    SetVerticesDirty();
 059    UpdateRangeTexts();
 060  }
 61
 062  protected override void OnPopulateMesh(VertexHelper vh) {
 063    vh.Clear();
 64
 65    // Convert to km for UI scale.
 066    float maxRange = _rangeScales[_currentRangeIndex] / 1000f;
 67
 68    // Get the rect dimensions.
 069    Rect rect = GetPixelAdjustedRect();
 070    Vector2 center = rect.center;
 71
 72    // Adjust scale based on rect size.
 073    _currentScaleFactor = Mathf.Min(rect.width, rect.height) / (2 * maxRange);
 74
 075    float scaleFactor = _currentScaleFactor;
 76
 77    // Draw the grid.
 078    DrawRangeRings(vh, center, maxRange, scaleFactor);
 079    DrawBearingLines(vh, center, maxRange, scaleFactor);
 80
 81    // Only update positions of existing range texts.
 082    UpdateRangeTextsPositions(center, maxRange, scaleFactor);
 083  }
 84
 85  private void DrawRangeRings(VertexHelper vh, in Vector2 center, float maxRange,
 086                              float scaleFactor) {
 87    // Extend the range rings to 10x the major marker range.
 088    float extendedMaxRange = maxRange * 10;
 89
 090    for (int i = 1; i <= _numberOfRangeRings * 10; ++i) {
 091      float radius = i * extendedMaxRange / (_numberOfRangeRings * 10);
 092      DrawCircle(vh, center, radius * scaleFactor, 128, 1f);
 93
 94      // Make every 10th ring thicker to indicate major markers.
 095      if (i % 10 == 0) {
 096        DrawCircle(vh, center, radius * scaleFactor, 128, 2f);
 097      }
 098    }
 099  }
 100
 101  private void DrawBearingLines(VertexHelper vh, Vector2 center, float maxRange,
 0102                                float scaleFactor) {
 103    // Extend the bearing lines to 10x the major marker range.
 0104    float extendedMaxRange = maxRange * 10;
 105
 0106    float angleStep = 360f / _numberOfBearingLines;
 107
 0108    for (int i = 0; i < _numberOfBearingLines; ++i) {
 0109      float angle = i * angleStep * Mathf.Deg2Rad;
 0110      Vector2 direction = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
 0111      DrawLine(vh, center, center + direction * extendedMaxRange * scaleFactor, _lineWidth);
 0112    }
 0113  }
 114
 115  private void DrawCircle(VertexHelper vh, in Vector2 center, float radius, int segments,
 0116                          float widthMultiplier) {
 0117    float angleStep = 360f / segments;
 0118    Vector2 prevPoint = center + new Vector2(radius, 0);
 119
 0120    for (int i = 1; i <= segments; ++i) {
 0121      float angle = i * angleStep * Mathf.Deg2Rad;
 0122      Vector2 newPoint = center + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
 0123      DrawLine(vh, prevPoint, newPoint, _lineWidth * widthMultiplier);
 0124      prevPoint = newPoint;
 0125    }
 0126  }
 127
 0128  private void DrawLine(VertexHelper vh, in Vector2 start, in Vector2 end, float thickness) {
 129    // Calculate the total scale factor from Canvas and RectTransform.
 0130    float totalScale = canvas.scaleFactor * transform.lossyScale.x;
 131
 132    // Thickness adjusted for total scale to maintain constant pixel width.
 0133    float adjustedThickness = thickness / totalScale;
 134
 0135    Vector2 direction = (end - start).normalized;
 0136    Vector2 perpendicular = new Vector2(-direction.y, direction.x) * adjustedThickness * 0.5f;
 137
 0138    Vector2 v0 = start - perpendicular;
 0139    Vector2 v1 = start + perpendicular;
 0140    Vector2 v2 = end + perpendicular;
 0141    Vector2 v3 = end - perpendicular;
 142
 0143    int index = vh.currentVertCount;
 144
 0145    UIVertex vertex = UIVertex.simpleVert;
 0146    vertex.color = _gridColor;
 147
 0148    vertex.position = v0;
 0149    vh.AddVert(vertex);
 150
 0151    vertex.position = v1;
 0152    vh.AddVert(vertex);
 153
 0154    vertex.position = v2;
 0155    vh.AddVert(vertex);
 156
 0157    vertex.position = v3;
 0158    vh.AddVert(vertex);
 159
 0160    vh.AddTriangle(index, index + 1, index + 2);
 0161    vh.AddTriangle(index + 2, index + 3, index);
 0162  }
 163
 0164  private void ClearRangeTexts() {
 0165    foreach (var text in _rangeTexts) {
 0166      if (text != null) {
 0167        DestroyImmediate(text.gameObject);
 0168      }
 0169    }
 0170    _rangeTexts.Clear();
 0171  }
 172
 0173  private void UpdateRangeTexts() {
 0174    ClearRangeTexts();
 175
 176    // Only create new labels if they do not exist.
 0177    if (_rangeTexts.Count == 0) {
 0178      for (int i = 1; i <= _numberOfRangeRings; ++i) {
 0179        GameObject textObj = Instantiate(_rangeText, transform);
 0180        TextMeshProUGUI textComponent = textObj.GetComponent<TextMeshProUGUI>();
 0181        textComponent.color = _textColor;
 0182        textComponent.transform.localScale = Vector3.one;
 0183        _rangeTexts.Add(textComponent);
 0184      }
 0185    }
 186
 187    // Update the text values.
 0188    for (int i = 0; i < _rangeTexts.Count; ++i) {
 0189      _rangeTexts[i].text = GetRangeLabelText(i + 1);
 0190    }
 0191  }
 192
 0193  private void UpdateRangeTextsPositions(in Vector2 center, float maxRange, float scaleFactor) {
 0194    if (_rangeTexts.Count == 0) {
 0195      return;
 196    }
 197
 0198    for (int i = 1; i <= _numberOfRangeRings; ++i) {
 0199      float radius = i * maxRange / _numberOfRangeRings;
 0200      float adjustedRadius = radius * scaleFactor;
 201
 202      // Position to the right (0 degrees).
 0203      Vector2 position = center + new Vector2(adjustedRadius, 0);
 204
 0205      TextMeshProUGUI textComponent = _rangeTexts[i - 1];
 0206      if (textComponent != null) {
 0207        textComponent.rectTransform.anchoredPosition = position;
 208        // Adjust text scale inversely to the grid's scaleFactor to maintain constant size
 209        // Assuming uniform scaling for both x and y
 0210        float inverseScale = 4f / scaleFactor;
 211        // textComponent.transform.localScale = new Vector3(inverseScale, inverseScale, 1f);
 0212      } else {
 213        // Remove the object if it doesn't have a TextMeshProUGUI component
 0214        _rangeTexts.Clear();
 0215        return;
 216      }
 0217    }
 0218  }
 219
 0220  private string GetRangeLabelText(int ringIndex) {
 0221    float maxRange = _rangeScales[_currentRangeIndex];
 0222    float rangeValue = ringIndex * maxRange / _numberOfRangeRings;
 223
 0224    if (rangeValue >= 1000f) {
 0225      return $"{rangeValue / 1000f:F0} km";
 0226    } else {
 0227      return $"{rangeValue:F0} m";
 228    }
 0229  }
 230}