2012-08-06 50 views
4

不知道如何使標題更具描述性,所以我只是從一個例子開始。我正在使用下面的代碼位來選擇一個枚舉的方向,這取決於與給定方向相比,四個軸中的哪一個形成了最小的角度。根據N值中的哪一個返回不同的結果

static Direction VectorToDirection(Vector2 direction) 
{ 
    double upDiff = System.Math.Acos(Vector2.Dot(direction, -Vector2.UnitY)); 
    double downDiff = System.Math.Acos(Vector2.Dot(direction, Vector2.UnitY)); 
    double leftDiff = System.Math.Acos(Vector2.Dot(direction, -Vector2.UnitX)); 
    double rightDiff = System.Math.Acos(Vector2.Dot(direction, Vector2.UnitX)); 

    double smallest = System.Math.Min(System.Math.Min(upDiff, downDiff), System.Math.Min(leftDiff, rightDiff)); 

    // This is the part I'm unsure about i.e. 
    // Comparing smallest with each value in turn 
    // To find out which of the four was "selected" 
    if (smallest == upDiff) return Direction.Up; 
    if (smallest == downDiff) return Direction.Down; 
    if (smallest == leftDiff) return Direction.Left; 
    return Direction.Right; 
} 

但我在最後得到關於浮點相等的resharper警告。我猜這不應該是一個問題,因爲執行Min,但想知道是否有更好的方式來解決這種問題除了比較smallest與每個原始值。

+0

謝謝你所有的答案,我有點不確定選擇哪一個。對於像這樣的一小部分值來說,像布蘭登一樣手動比較它們似乎是比我所做的更好的選擇。對於一般情況,我喜歡阿列克謝的方法,通過索引找到最小值,並將其標記爲更廣泛的答案。 – 2012-08-06 18:55:16

回答

1

我會把所有的選擇放在一個數組中,並找到最小指數。對於4種選擇,排序可能是過度的。如果此代碼的性能很重要 - 請確保爲不同的變體測量時間。下面

非編譯代碼:

static Direction VectorToDirection(Vector2 direction) 
{ 
    var directions = new Direction[]{ 
    Direction.Up, Direction.Down, Direction.Right, Direction.Left }; 
    var unit = new Vector2[] { 
    -Vector2.UnitY, Vector2.UnitY, Vector2.UnitX,-Vector2.UnitY}; 

    var minAngle = 10; 
    var minIndex = -1; 
    for(var index = 0; index < directions.length; index++) 
    { 
    double diff = System.Math.Acos(Vector2.Dot(direction, unit[index])); 
    if (diff < minAngle) 
    { 
     minAngle = diff; 
     minIndex = index; 
    } 

    return directions[minIndex]; 
} 
+0

似乎是一般情況下的最佳解決方案,其中存在大量或可變數量的值。 – 2012-08-06 18:49:54

0

集不準確

if ((Math.Abs(smallest - upDiff) < 0.00001) return Direction.Up; 
+2

我不認爲它適用於這種特殊情況 - 值將完全匹配其中一個選項,因爲double更像枚舉。該建議將解決resharper警告,但會添加不必要的代碼。 – 2012-08-06 17:59:00

1

你可以做的<double ,Direction>sort the dictionary一本字典,並用正確的枚舉返回獲得的最小值。

0

隨着你在做,我不認爲會有任何錯誤。但是,如果將來重構或更改代碼,則可能會遇到一些問題。

爲了安全起見,請執行resharper建議的操作。

2

這段代碼應該會得到你想要的結果。

if ((Math.Abs(direction.x) >= Math.Abs(direction.y)) 
     return direction.x >= 0 ? Direction.Right : Direction.Left; 
    return direction.y >= 0 ? Direction.Up : Direction.Down; 
+0

請注意,像您的原始代碼,這不特別處理邊緣情況。在你的代碼中,關係的優先順序是按順序向上向左向右,在我的代碼中,順序是右向左向上。 – mrranstrom 2012-08-06 18:23:40

+0

謝謝!雖然沒有直接回答我的問題,因爲代碼片段只是更廣泛問題的一個例子,它確實爲這種特定情況提供了更簡單高效的實現,並且我已經在我的代碼庫中進行了更改:) – 2012-08-06 18:39:52

1

可以定義一個包含diff和與之相關的值的類。 然後,您從這些對象中創建一個集合,並通過diff對它們進行排序。之後,您將返回與第一個元素關聯的值。

但是,我不會去那裏你的情況,代碼是明確的,因爲它是。如果可能值的數量大得多(或者事先不知道),那麼我纔會去尋找更通用的解決方案。

0

我通常使用switch而不是if-else(至少3個子句) - 有點簡潔和快速。

switch(smallest) { 
    case upDiff: return Direction.Up; 
    case downDiff: return Direction.Down; 
    case leftDiff: return Direction.Left; 
    default: return Direction.Right; 
} 
+0

'最小'不是一個整數類型,所以這個選項是:) – 2012-08-06 18:19:23

1

你能寫一些if語句嗎?

if (upDiff < leftDiff && upDiff < downDiff && upDiff < rightDiff) return Direction.Up; 
if (leftDiff < upDiff && leftDiff < downDiff && leftDiff < rightDiff) return Direction.Left; 
if (rightDiff < leftDiff && rightDiff < upDiff && rightDiff < downDiff) return Direction.Right; 
return Direction.Down; 

也許它可以進一步清理,但這似乎很簡單。

+0

這似乎是如此明顯,但對於這樣的情況下,沒有很多值的情況下,這確實打擊我作爲一個更好的解決方案比處理'Min'和平等比較。 – 2012-08-06 18:42:09

1
static Direction VectorToDirection(Vector2 direction) 
{ 
    var mappings = new[] 
    { 
     new { Direction = Direction.Up, Axis = -Vector2.UnitY }, 
     new { Direction = Direction.Down, Axis = Vector2.UnitY }, 
     new { Direction = Direction.Left, Axis = -Vector2.UnitX }, 
     new { Direction = Direction.Right, Axis = Vector2.UnitX } 
    }; 
    return mappings.OrderBy(m => Math.Acos(Vector2.Dot(direction, m.Axis))).Select(m => m.Direction).First(); 
} 

LINQ的方式。這沒有經過測試,但你應該得到它。

+0

這很有趣,它確實有效(除了你忘了添加'Acos')。我想知道是否有辦法讓'映射'靜態,以避免每次都重新創建它。因爲我不能在方法外使用'var',所以我無法弄清楚什麼類型可以選擇。 :) – 2012-08-06 18:33:14

+0

它實際上是anonymus類型......但是您可以輕鬆地使用Tuple [](或針對該問題的自定義POCO)作爲靜態只讀,而犧牲一些易讀性。請注意,OrderBy使用延遲執行,所以當只獲取第一個元素時,它不必排序整個枚舉值,而是查找最小值......這正是您想要的值。 – 2012-08-06 19:15:42

+0

@DavidGouveia:新增Math.Acos – 2012-08-06 19:19:47

相關問題