After some struggle I found it way more intuitive to process each side rather than each point. The reason is that the status of progress (whether a corner has met and where to go next) is more predictable in side-approach than point-approach.

```
public class Solution {
public IList<int> SpiralOrder(int[,] matrix) {
var n = matrix.GetLength(0);
var m = matrix.GetLength(1);
int ml = 0, nl = 0, mh = m - 1, nh = n - 1;
int count = 0, total = m * n;
var list = new int[total];
var axisX = true;
var ascending = true;
while (count < total) {
if (axisX && ascending) {
for (int i = ml; i <= mh; i++) list[count++] = matrix[nl, i];
axisX = false;
nl++;
}
else if (axisX && !ascending) {
for (int i = mh; i >= ml; i--) list[count++] = matrix[nh, i];
axisX = false;
nh--;
}
else if (!axisX && ascending) {
for (int i = nl; i <= nh; i++) list[count++] = matrix[i, mh];
axisX = true;
ascending = false;
mh--;
}
else if (!axisX && !ascending) {
for (int i = nh; i >= nl; i--) list[count++] = matrix[i, ml];
axisX = true;
ascending = true;
ml++;
}
}
return list;
}
}
```