2012-08-06 213 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