System.Drawing support?

Hi,

Perhaps it’s described somewhere but I couldn’t find any forumpost about it.

What’s the cross platform support for System.Drawing? I made a new OpenGL 3.8 project - and I can import System.Drawing wherever I want. However, I believe I’ve heard that System.Drawing was only supported for DirectX and not OpenGL. Also, i keep finding custom implementations of RectangleF and PointF here and there - and those would not be necessary if System.Drawing was free to use at any point?

Some clarification would be wonderful. Also if there is any other .Net namespaces I shouldn’t use in preparation for full crossplatform support, please let me know.

System.Drawing is only for GDI+ WinForms drawing, you can’t use it with MonoGame. I worked on a primitive drawing library a while back and it’s been imported into MonoGame.Extended. You can get it from there :slight_smile:

For PointF you just use Vector2, and for RectangleF I have a class I wrote a while ago that you can just use. I’ll post it here.

You’ll need both of these, SizeF and RectangleF. Just put them in their own files.

using Microsoft.Xna.Framework;

namespace ArbitraryPixel.Common.Drawing
{
    /// <summary>
    /// Represents a size structure, containing width and height.
    /// 
    /// This object is intended to reproduce the behaviour of System.Drawing.Size and
    /// Sytem.Drawing.SizeF, except use doubles for its internal storage.
    /// </summary>
    public struct SizeF
    {
        #region Private Members
        private float _width;
        private float _height;
        #endregion

        #region Static Members
        /// <summary>
        /// Represents an empty MTPSize object, where the width and height are zero.
        /// </summary>
        public static readonly SizeF Empty;
        #endregion

        #region Constructor(s)
        /// <summary>
        /// Constructs a new object.
        /// </summary>
        /// <param name="width">The desired width.</param>
        /// <param name="height">The desired height.</param>
        public SizeF(float width, float height)
        {
            _width = width;
            _height = height;
        }

        /// <summary>
        /// Constructs a new object.
        /// </summary>
        /// <param name="value">The value that both width and height will be set to.</param>
        public SizeF(float value)
        {
            _width = value;
            _height = value;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// The width for this size object.
        /// </summary>
        public float Width
        {
            get { return _width; }
            set { _width = value; }
        }

        /// <summary>
        /// The height for this object.
        /// </summary>
        public float Height
        {
            get { return _height; }
            set { _height = value; }
        }

        /// <summary>
        /// Get a point representing the centre point of this SizeF object.
        /// </summary>
        public Vector2 Centre
        {
            get { return new Vector2(_width / 2f, _height / 2f); }
        }
        #endregion

        #region Operators and Overrides
        /// <summary>
        /// Compare two MTPSize objects for equality.
        /// </summary>
        /// <param name="left">The left hand side of the comparison.</param>
        /// <param name="right">The right hand side of the comparison.</param>
        /// <returns>True if the left and right are equivalent; that is, they have equal values. False otherwise.</returns>
        public static bool operator ==(SizeF left, SizeF right)
        {
            return (left.Width == right.Width && left.Height == right.Height);
        }

        /// <summary>
        /// Compare two MTPSize objects for inequality.
        /// </summary>
        /// <param name="left">The left hand side of the comparison.</param>
        /// <param name="right">The right hand side of the comparison.</param>
        /// <returns>True if the left and right side are not equivalent; that is, they have different values. False otherwise.</returns>
        public static bool operator !=(SizeF left, SizeF right)
        {
            return !(left == right);
        }

        /// <summary>
        /// Creates a string representation of this object.
        /// </summary>
        /// <returns>The string representation of this object.</returns>
        public override string ToString()
        {
            // This is based on the string format of System.Drawing.Size and System.Drawing.SizeF.
            return string.Format("{{Width={0}, Height={1}}}", _width, _height);
        }

        /// <summary>
        /// Checks to see if an object is equal to this object.
        /// </summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>True if the supplied object is equal to this one, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            bool isEqual = false;

            if (obj is SizeF)
            {
                isEqual = ((SizeF)obj) == this;
            }

            return isEqual;
        }

        /// <summary>
        /// Get the hash code for this object.
        /// </summary>
        /// <returns>The hash code for this object.</returns>
        public override int GetHashCode()
        {
            int hash = 17;
            unchecked
            {
                hash = hash * 29 + _width.GetHashCode();
                hash = hash * 29 + _height.GetHashCode();
            }

            return hash;
        }

        /// <summary>
        /// Cast a SizeF object to a Vector2 object.
        /// </summary>
        /// <param name="size">The SizeF object to cast.</param>
        /// <returns>The SizeF object as an equivalent Vector2 object.</returns>
        public static implicit operator Vector2(SizeF size)
        {
            return new Vector2(size.Width, size.Height);
        }

        /// <summary>
        /// Cast a Vector2 object to a SizeF object.
        /// </summary>
        /// <param name="vector">The Vector2 object to cast.</param>
        /// <returns>The Vector2 object as an equivalent SizeF object.</returns>
        public static explicit operator SizeF(Vector2 vector)
        {
            return new SizeF(vector.X, vector.Y);
        }

        /// <summary>
        /// Cast a SizeF object to a Point object.
        /// </summary>
        /// <param name="size">The SizeF object to cast.</param>
        /// <returns>The SizeF object as an equivalent Point object.</returns>
        public static explicit operator Point(SizeF size)
        {
            return new Point((int)size.Width, (int)size.Height);
        }

        /// <summary>
        /// Cast a Point object to a SizeF object.
        /// </summary>
        /// <param name="p">The Point object to cast.</param>
        /// <returns>The Point object as an equivalent SizeF object.</returns>
        public static explicit operator SizeF(Point p)
        {
            return new SizeF(p.X, p.Y);
        }

        /// <summary>
        /// Multiply a SizeF object by a float.
        /// </summary>
        /// <param name="lhs">The SizeF object.</param>
        /// <param name="rhs">The float.</param>
        /// <returns>A SizeF object that is the result of the multiplication of the float by both the Width and Height. Effectively a scale operation.</returns>
        public static SizeF operator *(SizeF lhs, float rhs)
        {
            return new SizeF(lhs.Width * rhs, lhs.Height * rhs);
        }

        /// <summary>
        /// Multiply a SizeF object by another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the product of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator *(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width * rhs.Width, lhs.Height * rhs.Height);
        }

        /// <summary>
        /// Divide a SizeF object by a float.
        /// </summary>
        /// <param name="lhs">The SizeF object.</param>
        /// <param name="rhs">The float.</param>
        /// <returns>A new SizeF object that is the result of the division by the float of the Width and the Height of the SizeF object.</returns>
        public static SizeF operator /(SizeF lhs, float rhs)
        {
            return new SizeF(lhs.Width / rhs, lhs.Height / rhs);
        }

        /// <summary>
        /// Divide a SizeF object by another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the quotient of the supplied object's Width and Height respectively.</returns>
        public static SizeF operator /(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width / rhs.Width, lhs.Height / rhs.Height);
        }

        /// <summary>
        /// Add a SizeF object with another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the sum of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator +(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width + rhs.Width, lhs.Height + rhs.Height);
        }

        /// <summary>
        /// Subtract a SizeF object from another SizeF object.
        /// </summary>
        /// <param name="lhs">The first SizeF object.</param>
        /// <param name="rhs">The second SizeF object.</param>
        /// <returns>A new SizeF object where the Width and Height are equal to the difference of the supplied objects' Width and Height respectively.</returns>
        public static SizeF operator -(SizeF lhs, SizeF rhs)
        {
            return new SizeF(lhs.Width - rhs.Width, lhs.Height - rhs.Height);
        }

        /// <summary>
        /// Multiplies both the width and height by -1.
        /// </summary>
        /// <param name="value">The SizeF object to operate on.</param>
        /// <returns>The SizeF object where the Width and Height have been multiplied by -1.</returns>
        public static SizeF operator -(SizeF value)
        {
            return new SizeF(-value.Width, -value.Height);
        }
        #endregion
    }
}

using Microsoft.Xna.Framework;
using System;

namespace ArbitraryPixel.Common.Drawing
{
    /// <summary>
    /// Represents a rectangle structure, having both a location, as well a width and height.
    /// The width and height of a rectangle are relative to the location; therefore, they can
    /// be negative.
    /// 
    /// This object is intended to reproduce the behaviour of System.Drawing.Rectangle and
    /// Sytem.Drawing.RectangleF, except use doubles for its internal storage.
    /// </summary>
    public struct RectangleF
    {
        #region Private Members
        private Vector2 _location;
        private SizeF _size;
        #endregion

        #region Constructor(s)
        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="location">The location of this rectangle.</param>
        /// <param name="size">The size of this rectangle.</param>
        public RectangleF(Vector2 location, SizeF size)
        {
            _location = location;
            _size = size;
        }

        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="p1">The first corner of this rectangle.</param>
        /// <param name="p2">The second corner of this rectangle.</param>
        public RectangleF(Vector2 p1, Vector2 p2)
            : this(p1, new SizeF(p2.X - p1.X, p2.Y - p1.Y))
        {
        }

        /// <summary>
        /// Create a new rectangle object.
        /// </summary>
        /// <param name="x">The X coordinate of the location.</param>
        /// <param name="y">The Y coordinate of the location.</param>
        /// <param name="width">The width of this rectangle.</param>
        /// <param name="height">The height of this rectangle.</param>
        public RectangleF(float x, float y, float width, float height)
            : this(new Vector2(x, y), new SizeF(width, height))
        {
        }
        #endregion

        #region Static Members
        /// <summary>
        /// Represents an empty rectangle, where the location and size are initialized to zero.
        /// </summary>
        public static readonly RectangleF Empty;
        #endregion

        #region Public Properties
        /// <summary>
        /// The location of this rectangle.
        /// </summary>
        public Vector2 Location
        {
            get { return _location; }
            set { _location = value; }
        }

        /// <summary>
        /// The size of this rectangle.
        /// </summary>
        public SizeF Size
        {
            get { return _size; }
            set { _size = value; }
        }

        /// <summary>
        /// The X coordinate of this rectangle's location.
        /// </summary>
        public float X
        {
            get { return _location.X; }
            set { _location.X = value; }
        }

        /// <summary>
        /// The Y coordinate of this rectangle's location.
        /// </summary>
        public float Y
        {
            get { return _location.Y; }
            set { _location.Y = value; }
        }

        /// <summary>
        /// The width of this rectangle.
        /// </summary>
        public float Width
        {
            get { return _size.Width; }
            set { _size.Width = value; }
        }

        /// <summary>
        /// The height of this rectangle.
        /// </summary>
        public float Height
        {
            get { return _size.Height; }
            set { _size.Height = value; }
        }

        /// <summary>
        /// The location of this rectangle's opposite corner (relative to its location).
        /// </summary>
        public Vector2 OppositeCorner
        {
            get { return new Vector2(_location.X + _size.Width, _location.Y + _size.Height); }
        }

        /// <summary>
        /// The Y coordinate for this rectangle's top edge.
        /// </summary>
        public float Top { get { return this.Y; } }

        /// <summary>
        /// The X coordinate for this rectangle's left edge.
        /// </summary>
        public float Left { get { return this.X; } }

        /// <summary>
        /// The X coordinate for this rectangle's right edge.
        /// </summary>
        public float Right { get { return this.X + this.Width; } }

        /// <summary>
        /// The Y coordinate for this rectangle's bottom edge.
        /// </summary>
        public float Bottom { get { return this.Y + this.Height; } }

        /// <summary>
        /// The centre point of this rectangle.
        /// </summary>
        public Vector2 Centre
        {
            get
            {
                return new Vector2(this.Left + this.Width / 2f, this.Top + this.Height / 2f);
            }
        }
        #endregion

        #region Operators and Overloads
        /// <summary>
        /// Compare two rectangles for equality.
        /// </summary>
        /// <param name="left">The left rectangle.</param>
        /// <param name="right">The right rectangle.</param>
        /// <returns>True if both rectangles have the same location and size, false otherwise.</returns>
        public static bool operator ==(RectangleF left, RectangleF right)
        {
            Vector2 aMin = new Vector2(Math.Min(left.Left, left.Right), Math.Min(left.Top, left.Bottom));
            Vector2 aMax = new Vector2(Math.Max(left.Left, left.Right), Math.Max(left.Top, left.Bottom));

            Vector2 bMin = new Vector2(Math.Min(right.Left, right.Right), Math.Min(right.Top, right.Bottom));
            Vector2 bMax = new Vector2(Math.Max(right.Left, right.Right), Math.Max(right.Top, right.Bottom));

            return (aMin == bMin && aMax == bMax);
        }

        /// <summary>
        /// Compare two rectangles for inequality.
        /// </summary>
        /// <param name="left">The left rectangle.</param>
        /// <param name="right">The right rectangle.</param>
        /// <returns>True if both rectangles have different locations or sizes, false otherwise.</returns>
        public static bool operator !=(RectangleF left, RectangleF right)
        {
            return !(left == right);
        }

        /// <summary>
        /// Creates a string representation of this object.
        /// </summary>
        /// <returns>The string representation of this object.</returns>
        public override string ToString()
        {
            // This is based on the string format of System.Drawing.Rectangle and System.Drawing.RectangleF.
            return string.Format("{{X={0}, Y={1}, Width={2}, Height={3}}}", this.X, this.Y, this.Width, this.Height);
        }

        /// <summary>
        /// Checks to see if an object is equal to this object.
        /// </summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>True if the supplied object is equal to this one, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            bool isEqual = false;

            if (obj is RectangleF)
            {
                isEqual = ((RectangleF)obj) == this;
            }

            return isEqual;
        }

        /// <summary>
        /// Get the hash code for this object.
        /// </summary>
        /// <returns>The hash code for this object.</returns>
        public override int GetHashCode()
        {
            int hash = 17;
            unchecked
            {
                hash = hash * 29 + _location.GetHashCode();
                hash = hash * 29 + _size.GetHashCode();
            }

            return hash;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Adjust the edges of this rectangle by the specified amounts.
        /// </summary>
        /// <param name="horizontalAmount">The amount to adjust the left and right edges.</param>
        /// <param name="verticalAmount">The amount to adjust the top and buttom edges.</param>
        public void Inflate(float horizontalAmount, float verticalAmount)
        {
            this.X -= horizontalAmount;
            this.Width += horizontalAmount * 2; ;

            this.Y -= verticalAmount;
            this.Height += verticalAmount * 2;
        }

        /// <summary>
        /// Adjust the edges of this rectangle by the specified size.
        /// </summary>
        /// <param name="amount">A SizeF value to adjust by, using Width as the horizontal amount and Height as the vertical amount.</param>
        public void Inflate(SizeF amount)
        {
            this.Inflate(amount.Width, amount.Height);
        }

        /// <summary>
        /// Tests to see if a point is contained within this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="point">The point to test.</param>
        /// <returns>True if the point is inside the rectangle, false otherwise.</returns>
        public bool Contains(Vector2 point)
        {
            Vector2 min = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 max = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            return (point.X >= min.X && point.X < max.X && point.Y >= min.Y && point.Y < max.Y);
        }

        /// <summary>
        /// Tests to see if a point is contained within this rectangle.
        /// </summary>
        /// <param name="x">The X coordinate of the point.</param>
        /// <param name="y">The Y coordinate of the point.</param>
        /// <returns>True if the point is inside the rectangle, false otherwise.</returns>
        public bool Contains(float x, float y)
        {
            return this.Contains(new Vector2(x, y));
        }

        /// <summary>
        /// Tests to see if a rectangle is contained within this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="rect">The rectangle to test.</param>
        /// <returns>True if the supplied rectangle is contained by this object, false otherwise.</returns>
        public bool Contains(RectangleF rect)
        {
            Vector2 thisMin = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 thisMax = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            Vector2 rectMin = new Vector2(Math.Min(rect.Left, rect.Right), Math.Min(rect.Top, rect.Bottom));
            Vector2 rectMax = new Vector2(Math.Max(rect.Left, rect.Right), Math.Max(rect.Top, rect.Bottom));

            return (rectMin.X >= thisMin.X && rectMax.X <= thisMax.X && rectMin.Y >= thisMin.Y && rectMax.Y <= thisMax.Y);
        }

        /// <summary>
        /// Tests to see if a rectangle intersects with this rectangle.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height are negative.
        /// </summary>
        /// <param name="rect">The rectangle to test.</param>
        /// <returns>True if this object and the supplied rectangle intersect, false otherwise.</returns>
        public bool IntersectsWith(RectangleF rect)
        {
            Vector2 thisMin = new Vector2(Math.Min(this.Left, this.Right), Math.Min(this.Top, this.Bottom));
            Vector2 thisMax = new Vector2(Math.Max(this.Left, this.Right), Math.Max(this.Top, this.Bottom));

            Vector2 rectMin = new Vector2(Math.Min(rect.Left, rect.Right), Math.Min(rect.Top, rect.Bottom));
            Vector2 rectMax = new Vector2(Math.Max(rect.Left, rect.Right), Math.Max(rect.Top, rect.Bottom));

            return !((rectMax.X <= thisMin.X || rectMin.X >= thisMax.X) || (rectMin.Y >= thisMax.Y || rectMax.Y <= thisMin.Y));
        }


        /// <summary>
        /// Generate a new rectangle that is the intersection of two rectangles; that is, the portion
        /// common to both rectangles (overlap).
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height values are negative.
        /// </summary>
        /// <param name="a">The first rectangle.</param>
        /// <param name="b">The second rectangle.</param>
        /// <returns>A rectangle representing the common area of the two rectangles.</returns>
        public static RectangleF Intersect(RectangleF a, RectangleF b)
        {
            RectangleF result = RectangleF.Empty;

            if (a.IntersectsWith(b))
            {
                Vector2 aMin = new Vector2(Math.Min(a.Left, a.Right), Math.Min(a.Top, a.Bottom));
                Vector2 aMax = new Vector2(Math.Max(a.Left, a.Right), Math.Max(a.Top, a.Bottom));

                Vector2 bMin = new Vector2(Math.Min(b.Left, b.Right), Math.Min(b.Top, b.Bottom));
                Vector2 bMax = new Vector2(Math.Max(b.Left, b.Right), Math.Max(b.Top, b.Bottom));

                result.X = Math.Max(aMin.X, bMin.X);
                result.Y = Math.Max(aMin.Y, bMin.Y);
                result.Width = Math.Min(aMax.X, bMax.X) - result.X;
                result.Height = Math.Min(aMax.Y, bMax.Y) - result.Y;
            }

            return result;
        }

        /// <summary>
        /// Generate a new rectangle that is the union of two rectangles; that is, the
        /// rectangle that would minimally contain both supplied rectangles.
        /// 
        /// NOTE: This method deviates from the System.Drawing equivalents. This method will work
        ///       with return the correct result when width and height values are negative.
        /// </summary>
        /// <param name="a">The first rectangle.</param>
        /// <param name="b">The second rectangle.</param>
        /// <returns>A rectangle representing the union of the two rectangles.</returns>
        public static RectangleF Union(RectangleF a, RectangleF b)
        {
            RectangleF result = RectangleF.Empty;

            Vector2 aMin = new Vector2(Math.Min(a.Left, a.Right), Math.Min(a.Top, a.Bottom));
            Vector2 aMax = new Vector2(Math.Max(a.Left, a.Right), Math.Max(a.Top, a.Bottom));

            Vector2 bMin = new Vector2(Math.Min(b.Left, b.Right), Math.Min(b.Top, b.Bottom));
            Vector2 bMax = new Vector2(Math.Max(b.Left, b.Right), Math.Max(b.Top, b.Bottom));

            result.X = Math.Min(aMin.X, bMin.X);
            result.Y = Math.Min(aMin.Y, bMin.Y);
            result.Width = Math.Max(aMax.X, bMax.X) - result.X;
            result.Height = Math.Max(aMax.Y, bMax.Y) - result.Y;

            return result;
        }
        #endregion

        #region Casting
        /// <summary>
        /// Cast a RectangleF object to a Rectangle object.
        /// </summary>
        /// <param name="rect">The RectangleF object to cast.</param>
        /// <returns>A Rectangle object which is equivalent to the supplied RectangleF object.</returns>
        public static explicit operator Rectangle(RectangleF rect)
        {
            return new Rectangle((int)Math.Floor(rect.X), (int)Math.Floor(rect.Y), (int)Math.Floor(rect.Width), (int)Math.Floor(rect.Height));
        }

        /// <summary>
        /// Cast a Rectangle object to a RectangleF object.
        /// </summary>
        /// <param name="rect">The Rectangle object to cast.</param>
        /// <returns>A RectangleF object which is equivalent to the supplied Rectangle object.</returns>
        public static explicit operator RectangleF(Rectangle rect)
        {
            return new RectangleF(rect.X, rect.Y, rect.Width, rect.Height);
        }
        #endregion
    }
}

I don’t know exactly what the cross platform support is but dotnet Core you have to install nuget packages to acccess System.Drawing types like Bitmap, Brush and such, I’ve tested Bitmap save and load in Windows, Linux and Mac, seems to work OK, for everything else I cannot say https://www.nuget.org/packages/System.Drawing.Common/

If it’s the primitives you’re after they can be found in a separate package, I think it’s fair to assume that they are cross platform since they are just data types https://www.nuget.org/packages/System.Drawing.Primitives/

What’s the purpose of multiplying by 17 by 29?

Radix conversion hashingEdit

Analogous to the way an ASCII or EBCDIC character string representing a decimal number is converted to a numeric quantity for computing, a variable length string can be converted as (x0ak−1+x1ak−2+…+xk−2a+xk−1). This is simply a polynomial in a non-zero “radix” a!=1 that takes the components (x0,x1,…,xk−1) as the characters of the input string of length k. It can be used directly as the hash code, or a hash function applied to it to map the potentially large value to the hash table size. The value of a is usually a prime number at least large enough to hold the number of different characters in the character set of potential keys. Radix conversion hashing of strings minimizes the number of collisions.[16] Available data sizes may restrict the maximum length of string that can be hashed with this method. For example, a 128-bit double long word will hash only a 26 character alphabetic string (ignoring case) with a radix of 29; a printable ASCII string is limited to 9 characters using radix 97 and a 64-bit long word. However, alphabetic keys are usually of modest length, because keys must be stored in the hash table. Numeric character strings are usually not a problem; 64 bits can count up to 1019, or 19 decimal digits with radix 10.

Hash function - Wikipedia

EDIT

Messed up the quoting, meh… fixed it

Then why can i access for example RectangleF in my pure OpenGL framework project? No other nugets.

Thanks m8, I’ve allready copied these 2 classes from you :wink:

Then why can I access System.Drawing.RectangleF without any other nuget packages but OpenGL framework? That makes me worried there are other namespaces that you “just cant use”? (but still have access to)

I looked into it. System.Drawing.Primitives is included by default when the target framework of your project is netcoreappx.x. It is not if you target netstandard however, then you have to use the nuget package.

Either way using those data types is almost certainly safe. Like I said before they’re probably just data so why would they need to do unsafe operations

1 Like

The short answer is because the internet said so! :smiley:

The slightly longer answer is that GetHashCode is supposed to return a unique-ish value for any given object for any given set of properties. Multiplying it like that shifts the bits around in order to spread the values around over the bit range. Consider when width is 1 and height is 2. You don’t want GetHashCode to return the same value for when the width is 2 and the height is 1.

When I googled this ages ago, those were simple numbers that worked well enough. I can’t find the original article… I have a link somewhere, in some code, but I didn’t include it when I made these classes a few years ago because I couldn’t find it then either. I just copied this from some scrap prototype code I used somewhere else. The best I could find is this…

He uses different primes there, but it’s the same ideal.

The longer answer, and the one that will probably require you to do more research, is what MrValentine posted. I don’t understand it, but maybe you will! :slight_smile:

1 Like

Sorry, I guess I should have been more specific. If you’re creating a Windows project you can pull in any .NET dll you like and use it. The data types, like RectangleF, will work just fine; however, the drawing routines (ie, Graphics.DrawLine won’t, because they use GDI+ rendering and require you to create a Graphics object that, ultimately, draws to an HDC object… I think. It’s been a long time since I’ve looked under the hood of GDI+. Anyway, it’s a different drawing system than MonoGame uses.

You might be able to get the window context of the MonoGame window and use System.Drawing to draw over top of your MonoGame stuff, but it’s not ideal. It’s easier to just use a SpriteBatch and draw your primitives that way.

I thought it was rolled into MonoGame.Extended, I seem to recall doing it there, but it looks like @craftworkgames refactored it to support 3d drawing techniques using the vertex buffer… yea? I swear you had the SpriteBatch Primitives2D stuff in there at one point, but maybe I’m wrong :smiley:
(*EDIT: Oh, I found this from a few years ago, yea they reworked it! :smiley: Vector-based grphics - is Monogame the way to go?)

Anyway, the original project repository looks like it’s dead. Best I could find is a link to the page my buddy had put up on his personal space…

However, it does look like many folks have been keeping copies of it around. Here’s a link to one that’s probably better maintained…

I took a copy of this maybe a month ago actually, when needed debug drawing for the font scaling prototype I worked on. It was easier than digging through my own source folders where there’s like 5 iterations of this damn thing floating around haha. :smiley: