Author Topic: C# Point Class code  (Read 30137 times)

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
C# Point Class code
« on: May 04, 2010, 07:02:22 PM »
C# doesn't (unlike Delphi) has a cartesian point class so I went ahead and wrote one. All code public domain blah blah blah. Should work on Mono as well:

/* Sample Point Class for C# for use in Roguelikes. By Dave Moore (starbog@NOSPAMgmail.com) */
/* All Code here is Public Domain - no copyright whatsoever, use it however you want */

using System;

namespace Map
{
    // List of cardinal directions useful in offset and coordinate calculations
    enum CardinalDirection { None = -1, N = 0, NE = 1, E = 2, SE = 3, S = 4, SW = 5, W = 6, NW = 7 };

    // Point Class to handle Map Functions
    class Point
    {
        // Default Constructor
        public Point()
        {
            // Set the internal values
            this.x = 0;
            this.y = 0;
        }

        // Standard Constructor
        public Point(int X, int Y)
        {
            // Set the internal values
            this.x = X;
            this.y = Y;
        }

        // Limit the values of the point to an additional supplied arbitrary
        // bounds - useful when working with calculated values
        public Point(int X, int Y, int Min, int Max)
        {
            // Set the internal values
            this.x = X;
            this.y = Y;

            // Limit the x value if necessary
            if (this.x > Max) { this.x = Max; }
            else if (this.x < Min) { this.x = Min; }

            // Limit the y value if necessary
            if (this.y > Max) { this.y = Max; }
            else if (this.y < Min) { this.y = Min; }
        }

        // Copy Constructor - we need to use this because unlike C++ we cannot
        // overload the assignment operator
        public Point(Point P2)
        {
            // If we have a valid reference
            if ((object)P2 != null)
            {
                // Set the internal values
                this.x = P2.X;
                this.y = P2.Y;
            }           
        }

        // Override the ToString method
        public override string ToString()
        {
            return string.Format("[{0},{1}]", this.x, this.y);
        }

        // Overload the equality operator
        public static bool operator ==(Point P1, Point P2)
        {
            // If we have a valid reference
            if ((object)P1 == null) { return false; }
            if ((object)P2 == null) { return false; }

            // Check for full equality on both values
            return (P1.x == P2.x && P1.y == P2.y);
        }

        // Overload the non-equality operator
        public static bool operator !=(Point P1, Point P2)
        {
            // If we have a valid reference
            if ((object)P1 == null) { return false; }
            if ((object)P2 == null) { return false; }

            // Check for inequality on either values
            return (P1.x != P2.x || P1.y != P2.y);
        }

        // Overload the equals operator
        public override bool Equals(System.Object P2)
        {
            // If we have a valid reference
            if ((object)P2 == null) { return false; }

            // Check we can cast the incoming object to a Point
            Point p = P2 as Point;
            if ((System.Object)p == null) { return false; }

            // Check for full equality on both values
            return (this.x == p.x && this.y == p.y);
        }

        // Provide a custom GetHashCode function (needed when the Equals operator is
        // overridden
        public override int GetHashCode()
        {
            // Use XOR
            return this.x ^ this.y;
        }

        // Provide an equivalent of an assignment operator
        public void Set(Point P2)
        {
            // If we have a valid reference
            if ((object)P2 != null)
            {
                // Set the internal values
                this.x = P2.X;
                this.y = P2.Y;
            }
        }

        // Provide another equivalent of an assignment operator
        public void Set(int X, int Y)
        { 
            // Set the internal values
            this.x = X;
            this.y = Y;
        }

        // Return the euclidean distance between two points
        public int DistanceTo(Point P2)
        {
            // If we have a valid reference
            if ((object)P2 == null) { return -1; }

            // Return the distance (as an int, rounded down)

            return (int)Math.Sqrt((this.x - P2.x) * (this.x - P2.x) +
                (this.y - P2.y) * (this.y - P2.y));
        }

        // get the difference between two points
        public void SetFromOffset(Point P1, Point P2)
        {
            // Set the default values
            this.X = 0;
            this.Y = 0;

            // If we have a valid reference
            if ((object)P1 == null) { return; }
            if ((object)P2 == null) { return; }

            // Get the offsets
            this.X = (P1.x - P2.x);
            this.Y = (P1.y - P2.y);
        }

        // Return the difference between two points optionally limiting the
        // values returned
        public void SetFromOffset(Point P1, Point P2, int Min, int Max)
        {
            // Set the default values
            this.X = 0;
            this.Y = 0;

            // If we have a valid reference
            if ((object)P1 == null) { return; }
            if ((object)P2 == null) { return; }

            // Get the offsets
            this.X = (P1.x - P2.x);
            this.Y = (P1.y - P2.y);
     
            // Limit the x value if necessary
            if (this.X > Max) { this.X = Max; }
            else if (this.X < Min) { this.X = Min; }

            // Limit the y value if necessary
            if (this.Y > Max) { this.Y = Max; }
            else if (this.Y < Min) { this.Y = Min; }
        }

        // Get the direction of one point from another as an enum
        public CardinalDirection DirectionTo(Point P2)
        {
            // If we have a valid reference
            if ((object)P2 == null) { return CardinalDirection.None; }

            // Set up an offset array to convert the offsets of the two points
            // into a direction
            Point[] Directions = new Point[8];
            Directions[(int)CardinalDirection.N] = new Point(0, -1);
            Directions[(int)CardinalDirection.NE] = new Point(1, -1);
            Directions[(int)CardinalDirection.E] = new Point(1, 0);
            Directions[(int)CardinalDirection.SE] = new Point(1, 1);
            Directions[(int)CardinalDirection.S] = new Point(0, 1);
            Directions[(int)CardinalDirection.SW] = new Point(-1, 1);
            Directions[(int)CardinalDirection.W] = new Point(-1, 0);
            Directions[(int)CardinalDirection.NW] = new Point(-1, -1);
           
            // Get the offset from one point to another
            Point P = new Point();
            P.SetFromOffset(this, P2, -1, 1);

            // Find the matching direction
            int Index = 0;
            foreach (Point Item in Directions)
            {
                if (Item == P) { return (CardinalDirection)Index; }
                else { Index++; }
            }

            // Return the null value just in case
            return CardinalDirection.None;
        }

        // Check if two points are adjacent to each other
        public bool Adjacent(Point P2)
        {
            // Test if the points are 1 square apart
            return (this.DistanceTo(P2) == 1);
        }

        // Private data members
        private int x;
        private int y;

        // Publically accessible properties
        public int X
        {
            get { return this.x; }
            set { this.x = value; }
        }       
        public int Y
        {
            get { return this.y; }
            set { this.y = value; }
        }


    }

}


And here's a console test app as well:


/* Sample Point Class for C# for use in Roguelikes. By Dave Moore (starbog@NOSPAMgmail.com) */
/* All Code here is Public Domain - no copyright whatsoever, use it however you want */

using System;
using System.Text;
using Map;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Point P1 = new Point(10, 11);
            Point P2 = new Point(24, 20);
            Point P3 = new Point(100, 5);

            System.Console.WriteLine("C# Point Class Demo\n*******************\n");
            System.Console.WriteLine("Defined P1 as (10, 11), P2 as (24, 20) and P3 as (100, 5)\n");
            System.Console.WriteLine("Standard Constructor & ToString(): P1 = " + P1.ToString() + ", P2 = " + P2.ToString() + ", P3 = " + P3.ToString());
            Point P5 = new Point(999, -999, -9, 9);
            System.Console.WriteLine("Limit Constructor: P5 = (999, -999, -9, 9): P5 = " + P5.ToString());
            Point P4 = new Point(P1);
            System.Console.WriteLine("Copy Constructor (P4->P1): P4 = " + P4.ToString() + "\n");
            System.Console.WriteLine("==: P1 == P2 = " + (P1 == P2).ToString());
            System.Console.WriteLine("!=: P1 != P2 = " + (P1 != P2).ToString());
            System.Console.WriteLine("Equals(): P1.Equals(P2) = " + (P1.Equals(P2)).ToString());
            Point P6 = new Point(66, 66);
            P6.Set(P3);
            System.Console.WriteLine("(Assignment Operator) Set(): P6 = (66, 66), P6.Set(P3); P6 = " + P6.ToString());
            Point P7 = new Point(77, 77);
            P7.Set(7, 7);
            System.Console.WriteLine("(Direct Set Operator) Set(): P7 = (77, 77), P7.Set(7, 7); P7 = " + P7.ToString() + "\n");
            System.Console.WriteLine("DistanceTo(): P1.Distance(P2) = " + (P1.DistanceTo(P2)).ToString());
            System.Console.WriteLine("Adjacent(): P1.Adjacent(P2) = " + (P1.Adjacent(P2)).ToString());

            Point P8 = new Point(88, 88);
            P8.SetFromOffset(P1, P2);
            System.Console.WriteLine("SetFromOffSet(): P8 = (88, 88), P8.SetFromOffset(P1, P2) = " + P8.ToString());
            Point P9 = new Point(99, 99);
            P9.SetFromOffset(P2, P1);
            System.Console.WriteLine("SetFromOffSet(): P9 = (99, 99), P9.SetFromOffset(P2, P1) = " + P9.ToString());
            Point P10 = new Point(10, 10);
            P10.SetFromOffset(P1, P2, -1, 1);
            System.Console.WriteLine("SetFromOffSet(): P10 = (10, 10), P10.SetFromOffset(P1, P2, -1, 1) = " + P10.ToString() + "\n");
            System.Console.WriteLine("DirectionTo(): P2.DirectionTo(P1) = " + P2.DirectionTo(P1).ToString());
            System.Console.WriteLine("DirectionTo(): P1.DirectionTo(P2) = " + P1.DirectionTo(P2).ToString());

            Console.ReadKey(true);
        }
    }
}



Enjoy!

Best,
P.

scaught

  • Newcomer
  • Posts: 18
  • Karma: +0/-0
    • View Profile
Re: C# Point Class code
« Reply #1 on: May 04, 2010, 09:01:20 PM »
        // Provide a custom GetHashCode function (needed when the Equals operator is
        // overridden
        public override int GetHashCode()
        {
            // Use XOR
            return this.x ^ this.y;
        }
Was your intention to have the hashes of [0,1] and [1,0] be the same?

Also, why the overkill here:
Quote
            Point[] Directions = new Point[8];
            Directions[(int)CardinalDirection.N] = new Point(0, -1);
            Directions[(int)CardinalDirection.NE] = new Point(1, -1);
            Directions[(int)CardinalDirection.E] = new Point(1, 0);
            Directions[(int)CardinalDirection.SE] = new Point(1, 1);
            Directions[(int)CardinalDirection.S] = new Point(0, 1);
            Directions[(int)CardinalDirection.SW] = new Point(-1, 1);
            Directions[(int)CardinalDirection.W] = new Point(-1, 0);
            Directions[(int)CardinalDirection.NW] = new Point(-1, -1);

            // Get the offset from one point to another
            Point P = new Point();
            P.SetFromOffset(this, P2, -1, 1);

            // Find the matching direction
            int Index = 0;
            foreach (Point Item in Directions)
            {
                if (Item == P) { return (CardinalDirection)Index; }
                else { Index++; }
            }
Is simple maths somehow less desirable?

(Even though it is C# <shudder> , there's no reason to tax the GC when you don't have to)

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
Re: C# Point Class code
« Reply #2 on: May 04, 2010, 11:00:39 PM »
Good points. I'll get another version written to sort those out.

Ta,
P.

dungeonmans

  • Newcomer
  • Posts: 39
  • Karma: +0/-0
  • Crush monsters, get loots.
    • View Profile
    • Dungeonmans
    • Email
Re: C# Point Class code
« Reply #3 on: May 04, 2010, 11:36:13 PM »
Hey that's very cool of you. I had to do something quite similar in the code for Dungeonmans. One thing I found useful was to make sure my point class (called LocValue) had overriden operatives that worked with Vector2s as well.
Dungeonmans: The Heroic Adventure Roguelike! http://www.dungeonmans.com

purestrain

  • Rogueliker
  • ***
  • Posts: 172
  • Karma: +0/-0
    • View Profile
Re: C# Point Class code
« Reply #4 on: May 05, 2010, 09:51:13 AM »
There is a Point-Struct, and there are good reasons for it beeing a struct or (at least) a immutable object.

Imagine a mutable class:

Point loc = entity.Location;
lop.translate(1,1); -> entity gets moved (potential unwanted side effect)

Casting a point to object and checking for null is.... terrifying (there is e.g. ReferenceEquals(null, obj). Copy constructor? There is a clone interface. Non-Equality is the same as just return !(foo==bar).


My guess: Use a vector class of established libraries instead.

roguedjack

  • Rogueliker
  • ***
  • Posts: 70
  • Karma: +0/-0
    • View Profile
    • Rogue Survivor blog
    • Email
Re: C# Point Class code
« Reply #5 on: May 07, 2010, 01:12:17 PM »
I agree with purestrain. I use the System.Drawing.Point struct. You can always extend it if needed with C# static extensions : you can add new operators etc...

purestrain

  • Rogueliker
  • ***
  • Posts: 172
  • Karma: +0/-0
    • View Profile
Re: C# Point Class code
« Reply #6 on: May 07, 2010, 04:51:56 PM »
Don't get me wrong; but extension methods are only usefull if used with LINQ. Even on MSDN it tells that you should use extension methods very sparse. I would write a vector struct with implicit conversion to point if needed.