I have multiple structs as follows: Vector2, Vector3, Vector4. Each struct has operator overloads defined for basic arithmetic operations as well as implicit and explicit casting.
So far, I have added all possible combinations in the Vector4 class:
public static Vector4 operator + (Vector4 v1, Vector4 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector4 v1, Vector3 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector3 v1, Vector4 v2) { return (new Vector4(...)); }
public static implicit operator Vector4 (Vector2 v) { return (new Vector4(v)); }
public static implicit operator Vector4 (Vector3 v) { return (new Vector4(v)); }
public static explicit operator Vector3 (Vector4 v) { return (new Vector3(v)); }
public static explicit operator Vector2 (Vector4 v) { return (new Vector2(v)); }
Is there a guideline as to which operators are a better fit in which struct? I can't imagine hurting performance either way but am interested in knowing what would be more intuitive for other developers if they came across this code. The number of operator combinations quickly goes into the dozens.
By the way, duplicating these operators in other classes is not causing a compile time error. I have not checked which implementation would be called but that's besides the point.
If each class represents two, three and four-dimensional vectors, I think it should be possible for you to reduce your code somewhat. That's because the definitions of vector arithmetic between vectors of differing dimension are redundant as long as you have the necessary implicit up-conversions. Thus operators like the following will not be needed:
public static Vector4 operator + (Vector4 v1, Vector3 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector3 v1, Vector4 v2) { return (new Vector4(...)); }
I'd also suggest making the lower-dimension vectors handle up-conversions to, and down-conversions from, higher dimension vectors. That's because down-conversion strips information, and the choice of how to do it should be in the "more limited" struct.
Thus, VectorI structs would need implicit up-conversions to all VectorI+J and explicit down-conversions to all VectorI-J structs. In addition, the VectorI structs would need to implement their own vector arithmetic. But since 'I' only has values 2, 3 and 4, that means:
Vector2 needs implicit conversions to Vector3 and Vector4, as well as explicit down-conversions from Vector3 and Vector4.
Vector3 needs implicit conversions to Vector4 as well as explicit down-conversions from Vector4.
Vector4 needs no conversions.
All 4 structs implement linear algebra methods for themselves, between vectors of the same dimension only.
I just tested this scheme and adding disparate Vector2, Vector3 and Vector4 structs works as expected with the implicit conversion being done.
Update
Just made a quick prototype implementation for addition, and all the cross-dimension additions work as expected:
public struct Vector2
{
public double x, y;
public Vector2(double x, double y)
{
this.x = x; this.y = y;
}
#region linear algebra
public static Vector2 operator +(Vector2 first, Vector2 second)
{
return new Vector2(first.x + second.x, first.y + second.y);
}
#endregion
#region conversions to/from higher dimensions
public static implicit operator Vector3(Vector2 v2)
{
return new Vector3(v2.x, v2.y, 0);
}
public static implicit operator Vector4(Vector2 v2)
{
return new Vector4(v2.x, v2.y, 0, 0);
}
public static explicit operator Vector2(Vector3 v3)
{
return new Vector2(v3.x, v3.y);
}
public static explicit operator Vector2(Vector4 v4)
{
return new Vector2(v4.x, v4.y);
}
#endregion
}
public struct Vector3
{
public double x, y, z;
public Vector3(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
}
#region linear algebra
public static Vector3 operator +(Vector3 first, Vector3 second)
{
return new Vector3(first.x + second.x, first.y + second.y, first.z + second.z);
}
#endregion
#region conversions to/from higher dimensions
public static implicit operator Vector4(Vector3 v3)
{
return new Vector4(v3.x, v3.y, v3.z, 0);
}
public static explicit operator Vector3(Vector4 v4)
{
return new Vector3(v4.x, v4.y, v4.z);
}
#endregion
}
public struct Vector4
{
public double x, y, z, w;
public Vector4(double x, double y, double z, double w)
{
this.x = x; this.y = y; this.z = z; this.w = w;
}
#region linear algebra
public static Vector4 operator +(Vector4 first, Vector4 second)
{
return new Vector4(first.x + second.x, first.y + second.y, first.z + second.z, first.w + second.w);
}
#endregion
}
The following test code then works OK:
public static class VectorHelper
{
public static void Test()
{
var v2 = new Vector2(5, 5);
var v3 = new Vector3(7, 7, 7);
var v4 = new Vector4(3, 3, 3, 3);
var res1 = v2 + v3;
Debug.Assert(res1.GetType().Name == "Vector3"); // No assert
var res2 = v3 + v4;
Debug.Assert(res2.GetType().Name == "Vector4"); // No assert
var res3 = v2 + v4;
Debug.Assert(res3.GetType().Name == "Vector4"); // No assert
Debug.Assert(res3.x == 8 && res3.y == 8 && res3.z == 3 && res3.w == 3); // No assert
}
}
Related
Wrote the following struct to minimize the number of types for vectors with different components:
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 3 * sizeof(T))]
public struct Vector3<T> where T : unmanaged
{
public T X, Y, Z;
public Vector3(T x, T y, T z)
{
X = x;
Y = y;
Z = z;
}
}
This is basically, to avoid writing numerous types such Vec3u8u8u8, Vec4i32i32i32 and so on.
Also because of the need to enforce the type, e.g. sometimes byte instead of int.
However, the above code won't compile because of sizeof(T), producing CS0233:
'identifier' does not have a predefined size, therefore sizeof can only be used in an unsafe context
In case you ask, these types won't be marshalled back and forth with unmanaged side.
But I will occasionally need to use Unsafe.SizeOf<T> or Marshal.SizeOf<T> on them.
Question:
Can you suggest a working alternative?
StructLayoutAttribute.Size is optional. If you don't set it, .NET will compute the size automatically. From the docs:
This field must be equal or greater than the total size, in bytes, of the members of the class or structure. This field is primarily for compiler writers who want to extend the memory occupied by a structure for direct, unmanaged access.
If I simply remove it from Vector3<T> like so:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Vector3<T> where T : unmanaged
{
public T X, Y, Z;
public Vector3(T x, T y, T z) => (X, Y, Z) = (x, y, z);
}
Then Unsafe.SizeOf<Vector3<T>() will work for any valid T such as int or double:
Console.WriteLine(Unsafe.SizeOf<Vector3<int>>()); // 12
Console.WriteLine(Unsafe.SizeOf<Vector3<double>>()); // 24
Demo #1 here.
However, Marshal.SizeOf<T>() simply does not support generics. (See demo #2 here). The docs are a little ambiguous about this. For Marshal.Sizeof(Type t) the docs remark:
Exceptions
ArgumentException
The t parameter is a generic type definition.
While there is no similar remark for Marshal.SizeOf<T>(), a check of the source code shows that the method throws whenever typeof(T) is generic.
So, what are your options?
Firstly, you could replace all uses of Marshal.SizeOf<T>() with Unsafe.SizeOf<T>() in your codebase, and continue using your Vector3<T> struct.
Secondly, if you must continue to use Marshal.SizeOf<T>(), you may need to create non-generic types like Vector3Double and Vector3Int for every required component type. However, you could reduce duplicated code by extracting an interface that defines a minimal set of operations for vectors, then extract any additional required code into helper methods. .NET 7's generic math makes this fairly straightforward.
First define an vector interface with some minimal required operations like so:
public interface IVector3<TVector, TValue> : IAdditionOperators<TVector, TVector, TVector>, IAdditiveIdentity<TVector, TVector>, IMultiplyOperators<TVector, TValue, TVector>
where TValue : unmanaged, INumber<TValue>
where TVector : IVector3<TVector, TValue>, new ()
{
TValue X { get; init; }
TValue Y { get; init; }
TValue Z { get; init; }
}
Next, implement for your required component value types e.g.:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Vector3Double : IVector3<Vector3Double, double>
{
public double X { get; init; }
public double Y { get; init; }
public double Z { get; init; }
public Vector3Double(double x, double y, double z) => (X, Y, Z) = (x, y, z);
public static Vector3Double operator +(Vector3Double left, Vector3Double right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Vector3Double operator checked +(Vector3Double left, Vector3Double right) => checked(new(left.X + right.X, left.Y + right.Y, left.Z + right.Z));
public static Vector3Double operator *(Vector3Double left, double right) => new(right * left.X, right * left.Y, right * left.Z);
public static Vector3Double operator checked *(Vector3Double left, double right) => checked(new(right * left.X, right * left.Y, right * left.Z));
public static Vector3Double AdditiveIdentity => new Vector3Double();
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Vector3Int : IVector3<Vector3Int, int>
{
public int X { get; init; }
public int Y { get; init; }
public int Z { get; init; }
public Vector3Int(int x, int y, int z) => (X, Y, Z) = (x, y, z);
public static Vector3Int operator +(Vector3Int left, Vector3Int right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Vector3Int operator checked +(Vector3Int left, Vector3Int right) => checked(new(left.X + right.X, left.Y + right.Y, left.Z + right.Z));
public static Vector3Int operator *(Vector3Int left, int right) => new(right * left.X, right * left.Y, right * left.Z);
public static Vector3Int operator checked *(Vector3Int left, int right) => checked(new(right * left.X, right * left.Y, right * left.Z));
public static Vector3Int AdditiveIdentity => new Vector3Int();
}
And now you will be able to create extension methods base on these interfaces, for instance:
public static class VectorExtensions
{
public static TValue Dot<TVector, TValue>(this TVector left, TVector right) where TValue : unmanaged, INumber<TValue> where TVector : IVector3<TVector, TValue>, new ()
=> left.X + right.X + left.Y + right.Y + left.Z + right.Z;
public static TVector Sum<TVector, TValue>(this IEnumerable<TVector> vectors) where TValue : unmanaged, INumber<TValue> where TVector : IVector3<TVector, TValue>, new ()
=> vectors.Aggregate(TVector.AdditiveIdentity, (s, c) => s + c);
public static TVector Lerp<TVector, TValue>(TVector a, TVector b, TValue t) where TValue : unmanaged, INumber<TValue> where TVector : IVector3<TVector, TValue>, new ()
=> a * (TValue.MultiplicativeIdentity - t) + b*t;
}
With that done, you can do things like:
var vec1 = new Vector3Double(1.1, 1.1, 1.1);
var vec2 = new Vector3Double(2.2, 2.2, 2.2);
var sum = vec1 + vec2;
var scaled = vec1 * 2.2;
var dotP = vec1.Dot<Vector3Double, double>(vec2);
var aggregate = new [] { vec1, vec2 }.Sum<Vector3Double, double>();
Notes:
I was unable to get type inferencing to work automatically for the VectorExtensions methods, which is why I had to specify the generic parameters explicitly.
Your vector type is a struct, and Microsoft recommends that structs be immutable. So, in my IVector3 interface, I made X, Y and Z be init-only.
Demo #3 here.
This is my code I wrote a comment under the mistake. I am not allowed to do it in another way it should be two classes and it should be done in this way. If someone can help me i would appreciate this
Thank u
using System;
using MathLibrary;
namespace MathLibraryApp
{
class Program
{
static void Main(string[] args)
{
Vector v = new Vector();
Vector v1 = new Vector(4, 8, 12);
Vector v2 = new Vector(8,16,24);
Vector[] vectors = { v1, v2 };
Console.WriteLine(v.Add(vectors));
}
}
}
using System;
namespace MathLibrary
{
public class PointVectorBase
{
public PointVectorBase(double x=0 , double y=0 , double z=0 )
{
this.X = x;this.Y = y;this.Z = z;
}
protected virtual PointVectorBase CalculateSum(params Vector[] addends)
{
for (int i = 0; i < addends.Length; i++)
{
this.X = this.X + addends[i].X;
this.Y = this.Y + addends[i].Y;
this.Z = this.Z + addends[i].Z;
}
return this;
}
}
public class Vector : PointVectorBase
{
public Vector(double x = 0, double y = 0, double z = 0) : base(x, y, z){ }
public Vector Add(params Vector[] addends)
{
return this.CalculateSum(addends) ;
//Cannot implicitly convert type MathLibrary.PointVectorBase to MathLibrary.Vector. An explicit conversion exists (are you missing a cast?)
}
}
}
You can either cast the result like this:
public Vector Add(params Vector[] addends)
{
return this.CalculateSum(addends) As Vector;
}
This is dangerous though. Not all base vectors are vectors so you could have a null return. Same way as an animal is not always a cat in the public class cat: animal example.
Creating the implicit conversion is safer, though not always possible: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators
Your method CalculateSum returns value type PointVectorBase. Method Add in Vector class should return Vector.
Due to inheritance you can cast result of a CalculateSum to a Vector so it would be return this.CalculateSum(addends) as Vector;
In this case I wouldn't go for inheritance. You are only extending the base class with methods.
The problem on your CalculateSum is that you're returning this as a result. Which is a strange pattern. Either go for a void method which alters the current instance or return a new instance (so leave the current instance unmodified). I would go for the latter.
If your question is about inheritance, this is not a good example you gave.
But if you want an other way:
In your example I would go for extension methods. Also this is a nice case to use structs. By writing extension methods, you can extend the Vector3 with extra methods..
using System;
namespace MathLibrary
{
public struct Vector3
{
public double X;
public double Y;
public double Z;
public Vector3(double x=0 , double y=0 , double z=0 )
{
this.X = x;
this.Y = y;
this.Z = z;
}
public Vector3 CalculateSum(params Vector3[] addends)
{
var result = new Vector3();
for (int i = 0; i < addends.Length; i++)
{
result.X = result.X + addends[i].X;
result.Y = result.Y + addends[i].Y;
result.Z = result.Z + addends[i].Z;
}
return result;
}
}
public static class VectorExtensions
{
public static Vector3 Add(this Vector3 vector, params Vector3[] addends)
{
return vector.CalculateSum(addends);
// the add should actually add to the current vector,
// which makes it less readable.. calculate sum and add is almost the same.
return vector.CalculateSum(
new Vector3 [] { vector }
.Concat(addends)
.ToArray() );
}
}
}
The more your code has a functional approach the less strange things will happen.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I want to overload math and the array and [] to do mathematical operation
After a lot of up and back, the answer is the design of c# won't let you overload even for yourself 'build-in' types like you can in c++.
This is subject to a lot of debate and may emerge in the future as a feature.
Old c programmers moving (grudgingly to c#) will demand this. I demand this.
For example, I have lots of derived types for openGL such as Vertex x. I want to add them, make arrays of them, find them. Inherit them into bigger objects such as triangle or quad strips.
Specifically, I want to overload binary operators for =/ accumulator operators.
Below, I answer my question.
The secret is in C++ you can overload =/ TOKEN.
=/ is short for a=a/b. the operator =/ is ONE token.
In c# it is TWO tokens, and you can't overload the assignment(=) (use an implicit conversion or an explicit cast), you overload the
operator as a binary second token.
For example:
class Vertex{
public float x,y,z;
public Vertex(){get;set}
int some_small_int=2;
Vertex[] A=new Vertex[some_small_int];
Vertex[] B=new Vertex[some_small_int];
Vertex[] C=new Vertex[some_small_int];
public static Vertex[] operator+(Vertex[] A, Vertex[] B)
{
Vertex[] C=new Vertex[A.Count()];
for( int i=0;i< A.Count();i++)
{
C[i]=A[i]+B[i];
}
return C;
}
}
}
... insert into Vertex class...
array Vertex plus(array Vertex A, array Vertex B){
array Vertex C=new array<vertex>[A.Count()]; // B.Count() better be the same.
for(int i=0;i<A.Count();i++)
{
C[i].x=A[i].x+B[i].x;
C[i].y=A[i].y+B[i].y;
C[i].z=A[i].z+B[i].z;
}
}
Why can't do this in c#?
Because it is designed that way. I would have to write a class Float (as a wrapper for float).
Here is a whole listing for a Vector3 class in order to get ideas on how to implement operators and indexers.
[ImmutableObject(true)]
public struct Vector3 : IEnumerable<float>, ICloneable
{
readonly float x, y, z;
#region Definition
public Vector3(float x, float y, float z)
{
this.x=x;
this.y=y;
this.z=z;
}
public Vector3(double x, double y, double z)
: this((float)x, (float)y, (float)z) { }
public Vector3(Vector3 other)
{
this.x=other.x;
this.y=other.y;
this.z=other.z;
}
public Vector3(string description)
: this()
{
FromString(description);
}
/// <summary>
/// Indexer allows the use of the '[]' operator in Vector3
/// </summary>
/// <param name="index">The integer index 0-2</param>
/// <returns>A scalar value</returns>
public float this[int index]
{
get
{
switch (index)
{
case 0: return this.x;
case 1: return this.y;
case 2: return this.z;
}
throw new IndexOutOfRangeException();
}
}
public float X { get { return x; } }
public float Y { get { return y; } }
public float Z { get { return z; } }
public float Magnitude { get { return Norm(); } }
public float Norm() { return (float)Math.Sqrt(x*x+y*y+z*z); }
public Vector3 Normalized() { var m=Norm(); if (m>0) return this/m; return this; }
public static readonly Vector3 O=new Vector3(0, 0, 0);
public static readonly Vector3 I=new Vector3(1, 0, 0);
public static readonly Vector3 J=new Vector3(0, 1, 0);
public static readonly Vector3 K=new Vector3(0, 0, 1);
public static explicit operator float[](Vector3 vector)
{
return vector.ToArray();
}
#endregion
#region Math
public Vector3 Add(Vector3 other, float scale=1)
{
return new Vector3(
x+scale*other.x,
y+scale*other.y,
z+scale*other.z);
}
public Vector3 Scale(float scale)
{
return new Vector3(
scale*x,
scale*y,
scale*z);
}
public Vector3 Multiply(Matrix3 rhs)
{
return new Vector3(
X*rhs.A11+Y*rhs.A12+Z*rhs.A13,
X*rhs.A21+Y*rhs.A22+Z*rhs.A23,
X*rhs.A31+Y*rhs.A32+Z*rhs.A33);
}
public Vector3 Reciprocal(float numerator)
{
return new Vector3(numerator/x, numerator/y, numerator/z);
}
public static float Dot(Vector3 v1, Vector3 v2)
{
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z;
}
public static Vector3 Cross(Vector3 v1, Vector3 v2)
{
return new Vector3(
v1.y*v2.z-v1.z*v2.y,
v1.z*v2.x-v1.x*v2.z,
v1.x*v2.y-v1.y*v2.x);
}
public static float AngleBetween(Vector3 v1, Vector3 v2)
{
var cos=Dot(v1, v2);
var sin=Cross(v1, v2).Norm();
return (float)Math.Atan2(sin, cos);
}
public Vector3 AlongX() { return new Vector3(x, 0, 0); }
public Vector3 AlongY() { return new Vector3(0, y, 0); }
public Vector3 AlongZ() { return new Vector3(0, 0, z); }
public Vector3 AlongXY() { return new Vector3(x, y, 0); }
public Vector3 AlongYZ() { return new Vector3(0, y, z); }
public Vector3 AlongZX() { return new Vector3(x, 0, z); }
public Vector3 RotateAbout(Vector3 axis, float angle)
{
return Matrix3.RotateAbout(axis, angle)*this;
}
public Vector3 RotateAboutX(float angle)
{
float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);
return new Vector3(
x,
y*cos-z*sin,
y*sin+z*cos);
}
public Vector3 RotateAboutY(float angle)
{
float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);
return new Vector3(
x*cos+z*sin,
y,
-x*sin+z*cos);
}
public Vector3 RotateAboutZ(float angle)
{
float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);
return new Vector3(
x*cos-y*sin,
x*sin+y*cos,
z);
}
public Vector3 MirrorAboutXY() { return new Vector3(x, y, -z); }
public Vector3 MirrorAboutXZ() { return new Vector3(x, -y, z); }
public Vector3 MirrorAboutYZ() { return new Vector3(-x, y, z); }
#endregion
#region Operators
public static Vector3 operator+(Vector3 lhs, Vector3 rhs) { return lhs.Add(rhs); }
public static Vector3 operator-(Vector3 rhs) { return rhs.Scale(-1); }
public static Vector3 operator-(Vector3 lhs, Vector3 rhs) { return lhs.Add(rhs, -1); }
public static Vector3 operator*(float lhs, Vector3 rhs) { return rhs.Scale(lhs); }
public static Vector3 operator*(Vector3 lhs, float rhs) { return lhs.Scale(rhs); }
public static Vector3 operator/(Vector3 lhs, float rhs) { return lhs.Scale(1/rhs); }
public static Vector3 operator/(float lhs, Vector3 rhs) { return rhs.Reciprocal(lhs); }
public static float operator*(Vector3 lhs, Vector3 rhs) { return Dot(lhs, rhs); }
public static Vector3 operator^(Vector3 lhs, Vector3 rhs) { return Cross(lhs, rhs); }
public static Vector3 operator*(Vector3 lhs, Matrix3 rhs)
{
return lhs.Multiply(rhs);
}
#endregion
#region ICloneable Members
public Vector3 Clone() { return new Vector3(this); }
object ICloneable.Clone()
{
return Clone();
}
#endregion
#region IEnumerable<float> Members
public IEnumerator<float> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region IEquatable Members
/// <summary>
/// Equality overrides from <see cref="System.Object"/>
/// </summary>
/// <param name="obj">The object to compare this with</param>
/// <returns>False if object is a different type, otherwise it calls <code>Equals(Vector3)</code></returns>
public override bool Equals(object obj)
{
if (obj is Vector3)
{
return Equals((Vector3)obj);
}
return false;
}
/// <summary>
/// Checks for equality among <see cref="Vector3"/> classes
/// </summary>
/// <param name="other">The other <see cref="Vector3"/> to compare it to</param>
/// <returns>True if equal</returns>
public bool Equals(Vector3 other)
{
return x.Equals(other.x)
&&y.Equals(other.y)
&&z.Equals(other.z);
}
/// <summary>
/// Calculates the hash code for the <see cref="Vector3"/>
/// </summary>
/// <returns>The int hash value</returns>
public override int GetHashCode()
{
unchecked
{
return ((17*23+x.GetHashCode())*23+y.GetHashCode())*23+z.GetHashCode();
}
}
#endregion
#region IFormattable Members
public override string ToString()
{
return ToString("G");
}
public string ToString(string format)
{
return ToString(format, CultureInfo.CurrentCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
return string.Format("({0},{1},{2})",
x.ToString(format, formatProvider),
y.ToString(format, formatProvider),
z.ToString(format, formatProvider));
}
#endregion
#region Triangles
public static float TriangleArea(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 u=b-a, v=c-a;
Vector3 k=Vector3.Cross(u, v);
return k.Magnitude/2;
}
public static Vector3 TriangleNormal(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 u=b-a, v=c-a;
return Vector3.Cross(u, v).Normalized();
}
#endregion
#region IParsable Members
public void FromString(string description)
{
// "(X,Y,Z)" => (X,Y,Z)
description=description.Trim('(', ')');
var parts=description.Split(',');
if (parts.Length==3)
{
float new_x=0, new_y=0, new_z=0;
if (!float.TryParse(parts[0].Trim(), out new_x))
{
new_x=x;
}
if (!float.TryParse(parts[1].Trim(), out new_y))
{
new_y=y;
}
if (!float.TryParse(parts[2].Trim(), out new_z))
{
new_z=z;
}
this=new Vector3(new_x, new_y, new_z);
}
}
public float[] ToArray()
{
return new float[] { x, y, z };
}
#endregion
}
Some example usage here:
public TestVector()
{
Vector3 A=new Vector3(1, 2, 3);
Vector3[] array=new Vector3[100];
array[0]=A;
for (int i=1; i<100; i++)
{
array[i]=2*array[i-1]+Vector3.Cross(array[i], Vector3.I);
// or 2*array[i-1]+(array[i]^Vector3.I);
}
float Ax = A[0];
float max_x=array.Max((v) => v.X);
}
I now understand the issue.
The implementation of c# (=) assignment then (+) addition as two operators, not a single operator (=+) summation.
In c++ (=) is one token that is a unary operator that can be overloaded..
in c++
a=+b is shorthand for a=a+b
in c#
a=+b expands into a=a+b but it is not the equality operator that can be overloaded but the addition operator as a binary operator.
So the solution to overloading is to overload the plus, minus, multiplication, division, etc for the types required as binary operators, not unary operators.
Surprisingly, this seems to compile to compute the centroid of a Boxel Type, that consists of an array of edges, each edge has two vertices. I have yet to test the run time code but think it will work now.
public static Vertex operator / ( Vertex a , int b )
{
Vertex c = new Vertex ( );
c . x = a . x / b;
c . y = a . y / b;
c . z = a . z / b;
return c;
}
public static Vertex operator + ( Vertex a , Vertex b )
{
Vertex c = new Vertex ( );
c . x = a . x + b . x;
c . y = a . y + b . y;
c . z = a . z + b . z;
return c;
}
Vertex NewCentroid ( Boxel B )
{
Vertex C = new Vertex();
C = NewCentroid ( B . E );
return C;
}
Vertex NewCentroid ( Edge [ ] E )
{
Vertex C = new Vertex ( ){0.0,0.0,0.0};
foreach ( Edge e in E )
{
C **+** = NewCentroid ( e );
}
return C;
}
Vertex NewCentroid ( Edge e )
{
Vertex C = new Vertex ( );
C = NewCentroid ( e . V0 , e . V1 );
return C;
}
Vertex NewCentroid ( Vertex v0 , Vertex v1 )
{
Vertex C = new Vertex ( );
C = v0 **+** v1;
C =**/** 2.0;
return C;
}
Correct me if I am wrong. I'm old and decrepit in my programming ways.
Cap Sigma is the big greek letter usually taken as summation from downstairs subscript to upstairs superscript.
Now being symbol minded and old and decrepit I am happier.
I retract my accusation about c# being mathematically innumerate/illiterate.
You have
public static Vertex[] operator+(Vertex[] A, Vertex[] B)
{
return new vertex[] { A.x+B.x, A.y+B.y, A.z+B.z };
}
but
return new vertex[] { A.x+B.x, A.y+B.y, A.z+B.z };
is not a valid expression since for Vertex[] A there is no A.x.
Quick lambda solution:
return A.Zip(B, (v1, v2) => new Vertex(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z)).ToArray();
Whereas the latter new Vertex() expression could also be simplified if you overload the + operator of the Vertex object.
So, in no way is "Is c# mathematically illiterate/innumerate". Just a little bit of System.Linq needed to make it a nice expression.
This is basically component-wise vector addition implemented in C# with operator overloading. Also see http://www.dotnetperls.com/zip.
EDIT: Nope, in fact I was wrong. You can only overload operators from the enclosing class, so no direct overloads for a Vertex[] unless you declar Vertex[] as own wrapper class. But here is a complete, working code with some operator overloading and vector addition.
using System.IO;
using System;
using System.Linq;
class Vertex
{
public float x,y,z;
public Vertex(float _x, float _y,float _z) {
x = _x;
y = _y;
z = _y;
}
public static Vertex operator+(Vertex v1, Vertex v2)
{
return new Vertex(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);
}
public static Vertex[] AddVertices(Vertex[] A, Vertex[] B)
{
return A.Zip(B, (v1, v2) => v1 + v2).ToArray();
}
public override String ToString() { return string.Format("({0}, {1}, {2})",x,y,z);}
}
class Program
{
const int some_small_int=2;
static Vertex[] A=new Vertex[]{ new Vertex(10.0f, 10.0f, 10.0f),new Vertex(10.0f, 10.0f, 10.0f)};
static Vertex[] B=new Vertex[]{new Vertex(10.0f, 10.0f, 10.0f),new Vertex(10.0f, 10.0f, 10.0f)};
static Vertex[] C= Vertex.AddVertices(A,B);
static void Main()
{
Console.WriteLine("Vertex Array A:");
foreach(var vert in A) Console.WriteLine(vert);
Console.WriteLine("Vertex Array B:");
foreach(var vert in B) Console.WriteLine(vert);
Console.WriteLine("Vertex Array C:");
foreach(var vert in C) Console.WriteLine(vert);
var vert1 = new Vertex(1,2,3);
var vert2 = new Vertex(4,5,6);
var vert3 = vert1 + vert2;
Console.WriteLine("Vertex v1 + v2:" + vert3.ToString());
}
}
So, I am trying to override the "-" operator in c# to be able to subtract 2 vectors, but my class cannot implement Vector.
namespace Vectors
{
class VectorUtilv
{
private Point _p;
private Point _p2;
private Vector _v;
public Vector V
{
get { return _v; }
set { _v = value; }
}
public Point AddVector(Vector v)
{
_p.X = (_p.X + v.X);
_p2.Y = (_p.Y + v.Y);
return _p2;
}
// This is where I am trying to override but I cant add the v.X or
// the v.Y because it is not a vector. If i cast it as a vector the
// override doesn't work.
//////////////////////////////////////////////////////////////////
public static VectorUtilv operator -(Vector a, Vector b)
{
Vector v = new Vector();
v.X = a.X - b.X;
v.Y = a.Y - b.Y;
return v;
}
}
}
Any idea how I can remedy this issue?
Because you are Trying to define Operator for Class. At least one of its Parameters should be used in Operator with Type of your Class. for example you cant have class Car and define Operator witch only Gets int.
You can't override the operator for existing classes.only your own classes.
If you cant Modify Vector Class then you should declare your own class named Vector. or use the Type of your class for operator.
so you can have
class VectorUtilv
{
private Point _p;
private Point _p2;
private Vector _v;
public static VectorUtilv operator -(VectorUtilv a, VectorUtilv b)
{
//...
}
}
or
class Vecotr
{
private Point _p;
private Point _p2;
private Vector _v;
public static Vecotr operator -(Vecotr a, Vecotr b)
{
//...
}
}
But if you use solution 2. then you need to use qualifiers when using Vector.
System.Windows.Vector // is in Windows assembly
Vector // is your class
You can only override an operator in its own class.
Move all of that code to the Vector class.
In your vector class, override the '-' operator in it
public class Vector
{
public int X { get; set; }
public int Y { get; set; }
public static Vector operator -(Vector a, Vector b)
{
Vector v = new Vector();
v.X = a.X - b.X;
v.Y = a.Y - b.Y;
return v;
}
}
Then, you can uses it like that
Vector v1 = new Vector { X = 5, Y = 9};
Vector v2 = new Vector { X = 3, Y = 4 };
Vector vr = v1 - v2;
Console.WriteLine("Resultant Vector X: {0} & Y:{1}", vr.X, vr.Y);
I hope it will help you.
Thanks for the great responses. I wish I would I have checked before I figured it out. Would have saved me lots of time. I ended up doing pretty much exactly as you all said. Here is what I have.
public static VectorUtil operator -(VectorUtil aHeroPoint, VectorUtil bEnemyPoint) {
VectorUtil v = new VectorUtil();
v.Vec = new Vector(( aHeroPoint._p.X - bEnemyPoint._p.X),( aHeroPoint._p.Y - bEnemyPoint._p.Y));
return v;
}
http://codepaste.net/i87t39
The error I get is "One of the parameters of a binary operator must be the containing type"
public class Vector3D<T>
{
public T x;
public T y;
public T z;
public Vector3D()
{
}
public Vector3D(T a, T b, T c)
{
x = a; y = b; z = c;
}
/*public Vector3D(double a, double b, double c)
{
x = a; y = b; z = c;
}*/
public override string ToString()
{
//return base.ToString();
return String.Format("({0} {1} {2})", x , y , z);
}
public Vector3D<double> operator+( Vector3D<double> right)
{
Vector3D<double> vd = new Vector3D<double>() { x = 0, y = 0, z = 0};
vd.x = left.x + right.x;
vd.y = left.y + right.y;
vd.z = left.z + right.z;
return vd;
}
}
If I copy the code in your link:
public class Vector3D<T>
{
public T x;
public T y;
public T z;
public Vector3D()
{
}
public Vector3D(T a, T b, T c)
{
x = a; y = b; z = c;
}
public override string ToString()
{
//return base.ToString();
return String.Format("({0} {1} {2})", x , y , z);
}
public Vector3D<double> operator+( Vector3D<double> right)
{
Vector3D<double> vd = new Vector3D<double>() { x = 0, y = 0, z = 0};
vd.x = left.x + right.x;
vd.y = left.y + right.y;
vd.z = left.z + right.z;
return vd;
}
}
The error which you have is in operator+, because the containing type is Exp<T>, not Exp<double>. You should change it. Also there is no definition of left in this method !
Your method should like something like that:
public static Vector3D<T> operator +(Vector3D<T> right)
{
Vector3D<T> vd = new Vector3D<T>();
vd.x = right.x;
vd.y = right.y;
vd.z = right.z;
return vd;
}
Without a lot of extra work, you're not going to be able to implement a fully general-purpose generic Vector3D<T> class complete with mathematical operators. There are no constraints that you can provide for the generic type that will at once allow T to be a built-in numeric type and at the same time provide math operators for T.
There are a variety of ways to deal with this, such as special-casing the type in the generic class (yuck!), requiring T to implement an interface (i.e. wrap a normal numeric type in a type that implements the interface), or making the type abstract and requiring specialized subclasses to implement the operators as named methods (one of which could even depend on an interface, while others based directly on numeric types would just implement them directly).
For example:
abstract class Vector3D<T>
{
public readonly T x;
public readonly T y;
public readonly T z;
public Vector3D() { }
public Vector3D(T x, T y, T z)
{
this.x = x;
this.y = y;
this.z = z;
}
public abstract Vector3D<T> Add(Vector3D<T> right);
}
class Vector3DDouble : Vector3D<double>
{
public Vector3DDouble() { }
public Vector3DDouble(double x, double y, double z)
: base(x, y, z)
{ }
public override Vector3D<double> Add(Vector3D<double> right)
{
return new Vector3DDouble(x + right.x, y + right.y, z + right.z);
}
}
Assuming for the moment you've somehow addressed that issue, let's look at your operator + overload. First, your code won't even compile. You have overloaded the unary + operator, because you only have one parameter right for the overload, but in the method body you assume a second parameter left which is undeclared.
A more sensible implementation might look like this:
public static Vector3D<T> operator+(Vector3D<T> left, Vector3D<T> right)
{
return left.Add(right);
}
Note: I'm assuming here you've solved the arithmetic issue by requiring implementers to provide the arithmetic operations via named method, e.g. Add() per my example above. Obviously the exact implementation here would depend on how you dealt with the general issue of doing math with T values.