Beste måte å implementere disse 3 klasser i C #: Vector, Direction (enhetsvektor), Point

stemmer
5
  • Alle poeng er vektorer, og alle vektorer er Points.
  • Alle retninger er vektorer, ikke alle vektorer er beskrivelser (dette bør ikke bety både vei konvertering bør ikke være tillatt).

Jeg ønsker å ha operatørene styres gang for alle helst siden de er alle helt identiske. I C ++ jeg kan bare definere klassen vektor {float x, y, z; }, Og gjøre typedef Point = vektor, typedef Retning = vektor; I C # er det ingen tilsvarende ( ved hjelp Point = Vector, suger som du må plassere den i hvert enkelt dokument du bruker, og det er ikke håndheves av kompilatoren).

Jeg prøvde å definere 3 forskjellige klasser og overstyre operatørene for hver, og deretter gjøre implisitt type casting som ville gjøre koden kjøre saktere, etc.

Jeg prøvde å definere bare Vector, så Point: Vector og Retning: Vector, på denne måten jeg bare skrive operatørene en gang, men da kan jeg ikke gjøre implisitt type casting Point <-> Vector eller Direction <-> Vector.

Jeg kan ganske enkelt definere den vektor klasse og bruke det overalt, men som vil forårsake tvetydighet med hensyn til vær en variabel er ment å være en stilling i rommet (punkt), en relativ stilling i rommet (Vector) eller en enhetsvektor (Direction). For eksempel funksjonen:

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

Du kan ikke vite om det er ventet alle vektorer eller enhetsvektorer. I C ++ kan du gjøre det, så hvorfor ikke i C #?

Merk : å ha structs stedet for klasser ville være ideelt hvis mulig.

Publisert på 22/05/2009 klokken 11:42
kilden bruker
På andre språk...                            


6 svar

stemmer
10

Matematisk poeng er vektorer. Det er ingen absolutte punkter i rommet. Poeng blir definert som vektorer fra en vilkårlig opprinnelse. Så, jeg bruker vektorer for både poeng og forskjeller mellom punkter.

Fordi en retning er en enhetsvektor, er det ikke behov for en forskjell der heller. Det er som å prøve å definere forskjellige statiske typer for tallet 1 og andre heltall. Så jeg bruker vektorer for begge retninger og forskjeller mellom punkter.

Så, definerer en enkelt Vector type. Det vil gjøre livet ditt enklere, fordi du har færre klasser og overbelastede operatører / funksjoner for å skrive og teste, og vil være matematisk "renere" (hvis det betyr noe for deg).

Svarte 22/05/2009 kl. 12:40
kilden bruker

stemmer
8

På en purist nivå, vil jeg hevde at en Vectorog en Pointer ikke det samme, med algebra:

  • Point+ Vector=> Point(Oversettelse)
  • Vector+ Vector=> Vector(Addisjon)
  • Point+ Point(Ikke angitt)

Jeg ville ha 2 uforanderlige structs med implisitte (der de er logisk tilsvarende) eller eksplisitte (ellers) konverterings operatører. Jeg sannsynligvis ikke ville ha en Directionstruct, men ... Jeg har kanskje en Directioneiendom på en Vectorsom skalerer det til enhet, men som handler om det ...


Jeg har stumpet en primitiv Vectorog Pointvise samspillet; Jeg har ikke fylt ut alle Point(siden det er enklere):

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);
    }
}
Svarte 22/05/2009 kl. 11:50
kilden bruker

stemmer
6

Det er allerede Pointog Vectorimplementeringer i .Net> = 3,0. Dessverre the're i System.Windows.Media.Media3Dsom de er en del av WPF 3D API.

Disse eksisterende klasser har noen nyttige egenskaper som gjør dem verdt å studere (noen allerede nevnt av andre):

  • Point [+|-] Vector = Point
  • Vector [+|-] Vector = Vector
  • Point - Point = Vector
  • Vector har statiske metoder for å utføre punkt og kryssprodukter
  • Vector.Normalise()og vector.Negate()gjøre hva de sier på tinn

Kanskje du kan undersøke (og selv utvide) disse til eget bruk?

Svarte 22/05/2009 kl. 12:10
kilden bruker

stemmer
3

Jeg anbefaler mot å ha både en Vectorog en Pointtype. Selv om de er matematisk litt annerledes, betyr at skillet vanligvis ikke gi noen fordel i en programmeringssammenheng. Du kan også vurdere et punkt som en vektor fra origo til et punkt befinner seg.

Jeg anbefaler på det sterkeste å bruke verdityper i stedet for referansetypene, fordi du vanligvis bruker vektorer som du ville bruke reelle tall, så du vil ha de samme semantikk som en float (pass-by-verdi). Hvis du velger verdityper, husk at det er ofte bedre å gjøre dem uforanderlige.

Jeg fraråder også bruk av en Directiontype, og foretrekker å bruke normaliserte vektorer. I stedet for de tre Vector, Pointog Directiontyper, bare Vectorburde holde (hvis det er generisk nok).

Som for gjennomføring referanse, kan du ta en titt på XNA er Vector3 struktur .

Svarte 22/05/2009 kl. 12:43
kilden bruker

stemmer
1

Jeg foreslår å definere det slik:

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
}

Ting å merke seg:

  • Denne implementeringen er immutable.
  • En hvilken som helst type - Vector, Pointeller Directionkan tilsettes / subtraheres fra et hvilket som helst annet resulterer i en Vector.
  • En Directionkan multipliseres med en vektor.
  • De er implisitt utskiftbare - du kan lett endre det til eksplisitt utskiftbare ved å endre implicit operators til explicit operators.
Svarte 22/05/2009 kl. 13:15
kilden bruker

stemmer
1

Hvis vektorer er punkter og punkter er vektorer da de bør kanskje være den samme klassen (eller samme type struct).

Eller, hvis du vil ha dem til ...

  • Har de samme implementeringer av de samme metodene og operatører
  • Har forskjellige navn og ulike typer

... deretter definerer de metoder og operatører i en basisklasse med en beskyttet konstruktør, og definere punkt og vektor som blad subklasser av denne base klasse, slik at de arver sin funksjonalitet.

Hvis du ikke kan arv etc bruke til å gjøre akkurat hva du vil, og deretter kopiere og lime operatør definisjonene inn i flere klasser, fordi a) Det er ikke en vedlikehold propblem fordi de er neppe til å endre b) Du sier du har en gyldig grunn vil si "jeg gjør en raytracer så hastighet er kritisk".

Du kan si det spiller ingen rolle siden de er alle vektorer, men i C ++ kan du enkelt har æren, så hvorfor ikke C #?

Du kan skille i C # også: C # støtter definere forskjellige typer, etc. To ting du kan gjøre i C ++ som du ikke kan i C # er duck-skrevet maler og globale funksjoner, som kan være nyttig her, men er ikke avgjørende, dersom du er villig til stedet enten å bruke arv eller å kopiere og lime inn felles funksjonell.

Svarte 22/05/2009 kl. 11:46
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more