tags:

views:

1268

answers:

6
  • All Points are Vectors, and all Vectors are Points.
  • All Directions are Vectors, NOT all Vectors are Directions (this shouldn't mean both way conversion shouldn't be allowed).

I want to have the operators overridden once for all preferably since they're all completely identical. In C++ I can just define class Vector { float x,y,z; }, and do typedef Point = Vector, typedef Direction = Vector; In C# there is no equivalent ("using Point=Vector;" sucks as you have to place it in every single document you use, and it's not enforced by the compiler).

I tried to define 3 different classes and override the operators for each, then do implicit type casting which would make the code run slower, etc.

I tried defining just Vector, then Point:Vector and Direction:Vector, this way I only write the operators once but then I can't do implicit type casting Point <-> Vector or Direction <->Vector.

I could simply define the Vector class and use that everywhere, but that would create ambiguity as to weather a variable is supposed to be a position in space (Point), a relative position in space (Vector) or a unit vector (Direction). For example the function:

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

You can't know whether it's expecting any vectors or unit vectors. In C++ you could do that, so why not in C#?

So what's the best way to implement these in C#?

Note: having structs instead of classes would be ideal if possible.

+1  A: 

If vectors are points and points are vectors then maybe they should be the same class (or the same type of struct).

Or, if you want them to ...

  • Have the same implementations of the same methods and operators
  • Have different names and different types

... then define the methods and operators in a base class with a protected constructor, and define Point and Vector as leaf subclasses of this base class so that they inherit its functionality.

If you can't use inheritance etc to do exactly what you want, then copy-and-paste the operator definitions into the several classes, because a) It's not a maintenance propblem because they're unlikely to change b) You say you have a valid reason i.e. "I'm doing a raytracer so speed is critical".

You might say it doesn't matter since they're all vectors, but in C++ you could easily have the distinction, so why not C#?

You can distinguish in C# too: C# supports defining distinct types, etc. Two things you can do in C++ that you can't in C# are duck-typed templates and global functions, which might be helpful here but aren't essential, if you're willing instead either to use inheritance or to copy-and-paste the common functional.

ChrisW
Yes, but then I wouldn't know what a certain property might mean. For example: class Plane { Vector Position; Vector Normal; Vector Displace; }.Is Normal a normalized vector? Is Displace supposed to be a point or a vector, and if it's a vector is it normalized? You might say it doesn't matter since they're all vectors, but in C++ you could easily have the distinction, so why not C#?
manixrock
Edited to reply to your comment
ChrisW
+6  A: 

On a purist level, I'd argue that a Vector and a Point are not the same, by algebra:

  • Point + Vector => Point (translation)
  • Vector + Vector => Vector (addition)
  • Point + Point (not defined)

I would have 2 immutable structs with implicit (where they are logically equivalent) or explicit (otherwise) conversion operators. I probably wouldn't have a Direction struct, though... I might have a Direction property on a Vector that scales it to unit, but that is about it...


I've stubbed out a primitive Vector and Point to show the interactions; I haven't filled in all of Point (since it is simpler):

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);
    }
}
Marc Gravell
manixrock
+5  A: 

There are already Point and Vector implementations in .Net >= 3.0. Unfortunately the're in System.Windows.Media.Media3D as they're part of the WPF 3D API.

These existing classes do have some useful properties that make them worth studying (some already mentioned by others):

  • Point [+|-] Vector = Point
  • Vector [+|-] Vector = Vector
  • Point - Point = Vector
  • Vector has static methods for performing dot and cross products
  • Vector.Normalise() and vector.Negate() do what they say on the tin

Perhaps you could examine (and even extend) these for your own use?

Dan Vinton
That's pretty much what I want to do, except I don't want to use functions I want to use operators, so I can do: Vector proj = dir * (dir * normal); Vector reflected = dir - (n + n);
manixrock
+7  A: 

Mathematically, Points are Vectors. There are no absolute points in space. Points are defined as vectors from some arbitrary origin. So, I use Vectors for both points and differences between points.

Because a direction is a unit vector, there's no need for a distinction there either. It's like trying to define different static types for the integer 1 and other integers. So I use vectors for both directions and differences between points.

So, define a single Vector type. It'll make your life easier because you'll have fewer classes and overloaded operators/functions to write and test and will be mathematically "purer" (if that matters to you).

Nat
+3  A: 

I recommend against having both a Vector and a Point type. Although they are mathematically a bit different, that distinction does not usually provide any advantage in a programming context. Also, you can consider a point as being a vector from the origin to the point's location.

I strongly recommend using value types instead of reference types, because you usually use vectors as you'd use real numbers, so you'd want the same semantics as a float (pass-by-value). If you choose value types, remember that it is often better to make them immutable.

I also advise against using a Direction type, and prefer using normalized vectors. Instead of your three Vector, Point and Direction types, only Vector should suffice (if it is generic enough).

As for implementation reference, you might have a look at XNA's Vector3 structure.

Trillian
+1  A: 

I suggest defining it as such:

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
}

Things to note:

  • This implementation is immutable.
  • Any type - Vector, Point or Direction can be added/subtracted from any other resulting in a Vector.
  • A Direction can be multiplied with a vector.
  • They are implicitly interchangeable - you could easily change that to explicitly interchangeable by changing the implicit operators to explicit operators.
configurator