Лучший способ реализации этих 3 классов в C #: вектор, направление (единичный вектор), точка - PullRequest
5 голосов
/ 22 мая 2009
  • Все точки - это векторы, а все векторы - это точки.
  • Все направления являются векторами, а НЕ все векторы являются направлениями (это не должно означать, что нельзя разрешить преобразование в обоих направлениях).

Я хочу, чтобы операторы были переопределены раз и навсегда, желательно, поскольку все они полностью идентичны. В C ++ я могу просто определить класс Vector {float x, y, z; } и выполните typedef Point = Vector, typedef Direction = Vector; В C # нет эквивалента («using Point = Vector;» - отстой, так как вы должны поместить его в каждый используемый вами документ, и он не применяется компилятором).

Я попытался определить 3 разных класса и переопределить операторы для каждого из них, затем выполнить неявное приведение типов, что замедляло бы выполнение кода и т. Д.

Я попытался определить только Вектор, затем Точка: Вектор и Направление: Вектор, таким образом я пишу операторы только один раз, но тогда я не могу выполнить неявное приведение типа Точка <-> Вектор или Направление <-> Вектор.

Я мог бы просто определить класс Vector и использовать его повсеместно, но это создало бы двусмысленность в отношении того, должна ли переменная быть позицией в пространстве (Point), относительной позицией в пространстве (Vector) или единичным вектором ( Направление). Например, функция:

Vector GetOrthogon(Vector a, Vector b) {
    // ....
}

Вы не можете знать, ожидает ли он каких-либо векторов или единичных векторов. В C ++ вы могли бы сделать это, так почему бы не в C #?

Примечание : если это возможно, наличие структур вместо классов было бы идеальным.

Ответы [ 6 ]

10 голосов
/ 22 мая 2009

Математически, точки - это векторы. В космосе нет абсолютных точек. Точки определяются как векторы произвольного происхождения. Поэтому я использую Векторы как для точек, так и для различий между точками.

Поскольку направление является единичным вектором, в этом нет необходимости проводить различия. Это все равно что пытаться определить разные статические типы для целого числа 1 и других целых чисел. Поэтому я использую векторы как для направлений, так и для различий между точками.

Итак, определите один тип Vector. Это облегчит вашу жизнь, потому что у вас будет меньше классов и перегруженных операторов / функций для написания и тестирования, и они будут математически «чище» (если это важно для вас).

8 голосов
/ 22 мая 2009

На уровне пуристов я бы сказал, что Vector и Point не одинаковы по алгебре:

  • Point + Vector => Point (перевод)
  • Vector + Vector => Vector (дополнение)
  • Point + Point (не определено)

Я бы имел 2 неизменных структуры с неявными (где они логически эквивалентны) или явными (в противном случае) операторами преобразования. У меня, вероятно, не было бы структуры Direction, хотя ... Я мог бы иметь свойство Direction для Vector, которое масштабирует его до единицы, но это все ...


Я заглушил примитивы Vector и Point, чтобы показать взаимодействия; Я не заполнил все Point (так как это проще):

public struct Point {
    public Point(double x, double y) : this() { X = x; Y = y; }
    public double X { get; private set; }
    public double Y { get; private set; }

    // and more ;-p
}

public struct Vector : IEquatable<Vector> {
    public Vector(double x, double y) : this() { X = x; Y = y; }
    public Vector AsUnit() {
        if (X == 0 && Y == 0) throw new InvalidOperationException();
        if (X == 0) return new Vector(0, X > 0 ? 1 : -1);
        if (Y == 0) return new Vector(Y > 0 ? 1 : -1, 0);
        double sqr = Math.Sqrt((X * X) + (Y * Y));
        return new Vector(X / sqr, Y / sqr);
    }
    public double X { get; private set; }
    public double Y { get; private set; }

    public static  explicit operator Point(Vector vector) {
        return new Point(vector.X, vector.Y);
    }
    public static explicit operator Vector(Point point) {
        return new Vector(point.X, point.Y);
    }
    public override string ToString() {
        return "(" + X.ToString() + "," + Y.ToString() + ")";
    }
    public override int GetHashCode() {
        return 17 * X.GetHashCode() + Y.GetHashCode();
    }
    public override bool Equals(object obj) {
        return obj == null ? false : Equals((Vector)obj);
    }
    public bool Equals(Vector vector) {
        return X == vector.X && Y == vector.Y;
    }
    public static bool operator ==(Vector a, Vector b) {
        return a.X == b.X && a.Y == b.Y;
    }
    public static bool operator !=(Vector a, Vector b) {
        return a.X != b.X || a.Y != b.Y;
    }
    public static Point operator +(Point point, Vector vector) {
        return new Point(point.X + vector.X, point.Y + vector.Y);
    }
    public static Point operator -(Point point, Vector vector) {
        return new Point(point.X - vector.X, point.Y - vector.Y);
    }
    public static Vector operator +(Vector a, Vector b) {
        return new Vector(a.X + b.X, a.Y + b.Y);
    }
    public static Vector operator -(Vector a, Vector b) {
        return new Vector(a.X - b.X, a.Y - b.Y);
    }
}
6 голосов
/ 22 мая 2009

В .Net> = 3.0 уже есть реализации Point и Vector. К сожалению, вы находитесь в System.Windows.Media.Media3D, поскольку они являются частью WPF 3D API.

Эти существующие классы действительно имеют некоторые полезные свойства, которые делают их достойными изучения (некоторые уже упомянуты другими):

  • Point [+|-] Vector = Point
  • Vector [+|-] Vector = Vector
  • Point - Point = Vector
  • Vector имеет статические методы для выполнения точечных и перекрестных произведений
  • Vector.Normalise() и vector.Negate() делают то, что говорят на жестяной банке

Возможно, вы могли бы изучить (и даже расширить) их для собственного использования?

3 голосов
/ 22 мая 2009

Я рекомендую не иметь тип Vector и Point. Хотя они математически немного отличаются, это различие обычно не дает никаких преимуществ в контексте программирования. Кроме того, вы можете рассматривать точку как вектор от начала координат до ее местоположения.

Я настоятельно рекомендую использовать типы значений вместо ссылочных типов, потому что вы обычно используете векторы, как и действительные числа, поэтому вам нужна та же семантика, что и с плавающей точкой (передача по значению). Если вы выбираете типы значений, помните, что зачастую лучше сделать их неизменяемыми.

Я также не рекомендую использовать тип Direction и предпочитаю использовать нормализованные векторы. Вместо трех типов Vector, Point и Direction должно хватить только Vector (если оно достаточно универсальное).

Что касается ссылки на реализацию, вы можете взглянуть на Структура XNA Vector3 .

1 голос
/ 22 мая 2009

Предлагаю определить его так:

public abstract class VectorBase {
    public int X { get; private set; }
    public int Y { get; private set; }

    public VectorBase(int x, int y) {
        this.X = x;
        this.Y = y;
    }

    public VectorBase(VectorBase copy) : this(copy.X, copy.Y) {
        // creates a vector with the same x, y
    }

    public static Vector operator +(VectorBase left, VectorBase right) {
        return new Vector(left.X + right.X, left.Y + right.Y);
    }

    public static Vector operator -(VectorBase left, VectorBase right) {
        return new Vector(left.X - right.X, left.Y - right.Y);
    }
}

public class Vector : VectorBase {
    public Vector(VectorBase v) : base(v) { }
    public Vector(int x, int y) : base(x, y) { }
}

public class Point : VectorBase {
    public Point(VectorBase v) : base(v) { }
    public Point(int x, int y) : base(x, y) { }

    public static implicit operator Vector(Point p) {
         return new Vector(p);
    }

    public static implicit operator Point(Vector v) {
         return new Point(v);
    }
}

public class Direction : VectorBase {
    public Direction(VectorBase v) : base(v) { }
    public Direction(int x, int y) : base(x, y) { }

    public static implicit operator Vector(Direction d) {
         return new Vector(d);
    }

    public static implicit operator Direction(Vector v) {
         return new Direction(v);
    }

    // implementation of *, any other stuff you need
}

На что обратить внимание:

  • Это реализация immutable.
  • Любой тип - Vector, Point или Direction может быть добавлен / вычтен из любого другого, что приведет к Vector.
  • A Direction можно умножить на вектор.
  • Они неявно взаимозаменяемы - вы можете легко изменить их на явно взаимозаменяемые, изменив implicit operator s на explicit operator s.
1 голос
/ 22 мая 2009

Если векторы - это точки, а точки - это векторы, то, возможно, они должны быть одного и того же класса (или структуры одинакового типа).

Или, если вы хотите, чтобы они ...

  • Имеют одинаковые реализации тех же методов и операторов
  • имеют разные имена и разные типы

... затем определите методы и операторы в базовом классе с помощью защищенного конструктора и определите Point и Vector как конечные подклассы этого базового класса, чтобы они наследовали его функциональные возможности.

Если вы не можете использовать наследование и т. Д. Для выполнения именно того, что вам нужно, то скопируйте и вставьте определения операторов в несколько классов, потому что а) это не проблема обслуживания, поскольку они вряд ли изменятся б) вы скажем, у вас есть веская причина, то есть «я делаю raytracer, поэтому скорость критична».

Вы можете сказать, что это не имеет значения, поскольку все они являются векторами, но в C ++ вы можете легко иметь различие, так почему бы не C #?

Вы также можете различать в C #: C # поддерживает определение различных типов и т. Д. В C ++ вы можете делать две вещи, которые вы не можете делать в C #, это шаблоны с уткой и глобальные функции, которые могут быть полезны здесь, но не важно, если вы вместо этого хотите использовать наследование или скопировать и вставить общий функционал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...