458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.ComponentModel.DataAnnotations;
 | |
| using System.Diagnostics;
 | |
| 
 | |
| namespace Quik
 | |
| {
 | |
|     /// <summary>
 | |
|     /// A 2 dimensional Vector.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("({X}, {Y})")]
 | |
|     public struct QVec2
 | |
|     {
 | |
|         public float X;
 | |
|         public float Y;
 | |
| 
 | |
|         public float Magnitude => MathF.Sqrt(X * X + Y * Y);
 | |
| 
 | |
|         public QVec2(float x, float y)
 | |
|         {
 | |
|             X = x;
 | |
|             Y = y;
 | |
|         }
 | |
| 
 | |
|         public QVec2 Normalize() => this * (1.0f / Magnitude);
 | |
| 
 | |
|         public float Atan2() => MathF.Atan2(Y, X);
 | |
|         public static QVec2 operator +(QVec2 a, QVec2 b)
 | |
|         {
 | |
|             return new QVec2()
 | |
|             {
 | |
|                 X = a.X + b.X,
 | |
|                 Y = a.Y + b.Y
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static QVec2 operator -(QVec2 a)
 | |
|         {
 | |
|             return new QVec2()
 | |
|             {
 | |
|                 X = -a.X,
 | |
|                 Y = -a.Y
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static QVec2 operator -(QVec2 a, QVec2 b)
 | |
|         {
 | |
|             return new QVec2()
 | |
|             {
 | |
|                 X = a.X - b.X,
 | |
|                 Y = a.Y - b.Y
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static QVec2 operator *(float a, QVec2 b)
 | |
|         {
 | |
|             return new QVec2()
 | |
|             {
 | |
|                 X = a * b.X,
 | |
|                 Y = a * b.Y
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static QVec2 operator *(QVec2 a, float b) => b * a;
 | |
| 
 | |
|         public static bool operator ==(QVec2 a, QVec2 b) => a.X == b.X && a.Y == b.Y;
 | |
| 
 | |
|         public static bool operator !=(QVec2 a, QVec2 b) => a.X != b.X || a.Y != b.Y;
 | |
| 
 | |
|         public override bool Equals(object obj)
 | |
|         {
 | |
|             if (obj is QVec2)
 | |
|             {
 | |
|                 return (QVec2) obj == this;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public override int GetHashCode()
 | |
|         {
 | |
|             return 63671 * X.GetHashCode() ^ 81083 * Y.GetHashCode();
 | |
|         }
 | |
| 
 | |
|         public static float Dot(QVec2 a, QVec2 b)
 | |
|         {
 | |
|             return a.X * b.X + a.Y * b.Y;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A RGBA color value.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("({R}, {G}, {B}, {A})")]
 | |
|     public struct QColor
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Red channel.
 | |
|         /// </summary>
 | |
|         public byte R;
 | |
|         /// <summary>
 | |
|         /// Green channel.
 | |
|         /// </summary>
 | |
|         public byte G;
 | |
|         /// <summary>
 | |
|         /// Blue channel.
 | |
|         /// </summary>
 | |
|         public byte B;
 | |
|         /// <summary>
 | |
|         /// Alpha channel.
 | |
|         /// </summary>
 | |
|         public byte A;
 | |
| 
 | |
|         public QColor(byte r, byte g, byte b, byte a)
 | |
|         {
 | |
|             R = r;
 | |
|             G = g;
 | |
|             B = b;
 | |
|             A = a;
 | |
|         }
 | |
|         
 | |
|         public QColor(byte r, byte g, byte b) : this(r, g, b, 1) { }
 | |
| 
 | |
|         public QColor(uint hexCode)
 | |
|         {
 | |
|             R = (byte)((hexCode >> 24) & 0xFF);
 | |
|             G = (byte)((hexCode >> 16) & 0xFF);
 | |
|             B = (byte)((hexCode >> 8 ) & 0xFF);
 | |
|             A = (byte)((hexCode >> 0 ) & 0xFF);
 | |
|         }
 | |
|         public QColor(int hexCode) : this((uint)hexCode) { }
 | |
| 
 | |
|         public static readonly QColor Black = new QColor(0, 0, 0, 255);
 | |
|         public static readonly QColor Red = new QColor(255, 0, 0, 255);
 | |
|         public static readonly QColor Green = new QColor(0, 255, 0, 255);
 | |
|         public static readonly QColor Blue = new QColor(0, 0, 255, 255);
 | |
|         public static readonly QColor Yellow = new QColor(255, 255, 0, 255);
 | |
|         public static readonly QColor Cyan = new QColor(0, 255, 255, 255);
 | |
|         public static readonly QColor Magenta = new QColor(255, 0, 255, 255);
 | |
|         public static readonly QColor White = new QColor(255, 255, 255, 255);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A bezier curve segment.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("{Start} -- {ControlA} -- {ControlB} -- {End}")]
 | |
|     public struct QBezier
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Segment start point.
 | |
|         /// </summary>
 | |
|         public QVec2 Start;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Start point control point.
 | |
|         /// </summary>
 | |
|         public QVec2 ControlA;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// End point control point.
 | |
|         /// </summary>
 | |
|         public QVec2 ControlB;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Segment end point.
 | |
|         /// </summary>
 | |
|         public QVec2 End;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// An approximation of the arc length of the bezier curve, for calculating rasterization resolution.
 | |
|         /// </summary>
 | |
|         public float RasterizationArc => 
 | |
|             0.5f * (End - Start).Magnitude + 
 | |
|             0.5f * ((ControlA - Start).Magnitude + (ControlB - ControlA).Magnitude + (End - ControlB).Magnitude);
 | |
| 
 | |
|         public QBezier(QVec2 start, QVec2 controlA, QVec2 controlB, QVec2 end)
 | |
|         {
 | |
|             Start = start;
 | |
|             ControlA = controlA;
 | |
|             ControlB = controlB;
 | |
|             End = end;
 | |
|         }
 | |
| 
 | |
|         public QBezier(
 | |
|             float startX,
 | |
|             float startY,
 | |
|             float controlAx,
 | |
|             float controlAy,
 | |
|             float controlBx,
 | |
|             float controlBy,
 | |
|             float endX,
 | |
|             float endY)
 | |
|         : this(
 | |
|             new QVec2(startX, startY),
 | |
|             new QVec2(controlAx, controlAy),
 | |
|             new QVec2(controlBx, controlBy),
 | |
|             new QVec2(endX, endY))
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Get a point in the curve segment.
 | |
|         /// </summary>
 | |
|         /// <param name="t">Control parameter (between 0 and 1)</param>
 | |
|         /// <returns>The point on the curve.</returns>
 | |
|         public QVec2 GetBezierPoint(float t)
 | |
|         {
 | |
|             float T = 1 - t;
 | |
|             return
 | |
|                 T * T * T * Start +
 | |
|                 3 * T * T * t * ControlA +
 | |
|                 3 * T * t * t * ControlB +
 | |
|                 t * t * t * End;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Get the tangent on the curve.
 | |
|         /// </summary>
 | |
|         /// <param name="t">Control parameter (between 0 and 1)</param>
 | |
|         /// <returns>The tangent curve.</returns>
 | |
|         public QVec2 GetBezierTangent(float t)
 | |
|         {
 | |
|             float T = 1 - t;
 | |
|             return
 | |
|                 (
 | |
|                     3 * T * T * (ControlA - Start) +
 | |
|                     6 * T * t * (ControlB - ControlA) +
 | |
|                     3 * t * t * (End - ControlB)
 | |
|                 ).Normalize();
 | |
|         }
 | |
| 
 | |
|         internal QVec2 GetBezierNormal(float t)
 | |
|         {
 | |
|             QVec2 tangent = GetBezierTangent(t);
 | |
|             return new QVec2(-tangent.Y, tangent.X);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A line segment.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("{Start} -- {End}")]
 | |
|     public struct QLine
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Start point.
 | |
|         /// </summary>
 | |
|         public QVec2 Start;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// End point.
 | |
|         /// </summary>
 | |
|         public QVec2 End;
 | |
| 
 | |
|         public QLine(QVec2 start, QVec2 end)
 | |
|         {
 | |
|             Start = start;
 | |
|             End = end;
 | |
|         }
 | |
| 
 | |
|         public QLine(float startX, float startY, float endX, float endY)
 | |
|         {
 | |
|             Start.X = startX;
 | |
|             Start.Y = startY;
 | |
|             End.X = endX;
 | |
|             End.Y = endY;
 | |
|         }
 | |
| 
 | |
|         public QVec2 Normal()
 | |
|         {
 | |
|             QVec2 tangent = Tangent();
 | |
|             return new QVec2(-tangent.Y, tangent.X);
 | |
|         }
 | |
|         public QVec2 Tangent()
 | |
|         {
 | |
|             return (End - Start).Normalize();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A rectangle.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("({Right}, {Top}, {Left}, {Bottom})")]
 | |
|     public struct QRectangle
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Rectangle maximum point.
 | |
|         /// </summary>
 | |
|         public QVec2 Max;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Rectangle minimum point.
 | |
|         /// </summary>
 | |
|         public QVec2 Min;
 | |
| 
 | |
|         public float Left
 | |
|         {
 | |
|             get => Min.X;
 | |
|             set => Min.X = value;
 | |
|         }
 | |
| 
 | |
|         public float Right
 | |
|         {
 | |
|             get => Max.X;
 | |
|             set => Max.X = value;
 | |
|         }
 | |
| 
 | |
|         public float Top
 | |
|         {
 | |
|             get => Max.Y;
 | |
|             set => Max.Y = value;
 | |
|         }
 | |
| 
 | |
|         public float Bottom
 | |
|         {
 | |
|             get => Min.Y;
 | |
|             set => Min.Y = value;
 | |
|         }
 | |
| 
 | |
|         public QVec2 Size
 | |
|         {
 | |
|             get => Max - Min;
 | |
|             set => Max = Min + value;
 | |
|         }
 | |
| 
 | |
|         public QRectangle(QVec2 max, QVec2 min)
 | |
|         {
 | |
|             Max = max;
 | |
|             Min = min;
 | |
|         }
 | |
| 
 | |
|         public QRectangle(float r, float t, float l, float b)
 | |
|         {
 | |
|             Max = new QVec2() {X = r, Y = t};
 | |
|             Min = new QVec2() {X = l, Y = b};
 | |
|         }
 | |
| 
 | |
|         public bool Contains(QVec2 point)
 | |
|         {
 | |
|             return
 | |
|                 point.X > Left   && point.X < Right &&
 | |
|                 point.Y > Bottom && point.Y < Top;
 | |
|         }
 | |
| 
 | |
|         internal void Translate(in QVec2 offset)
 | |
|         {
 | |
|             Min += offset;
 | |
|             Max += offset;
 | |
|         }
 | |
| 
 | |
|         public static QRectangle Intersect(in QRectangle a, in QRectangle b) =>
 | |
|             new QRectangle(
 | |
|                 Math.Min(a.Right, b.Right),
 | |
|                 Math.Min(a.Top, b.Top),
 | |
|                 Math.Max(a.Left, b.Left),
 | |
|                 Math.Max(a.Bottom, b.Bottom)
 | |
|             );
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// An ellipse.
 | |
|     /// </summary>
 | |
|     /// <remarks>It is undefined to have an ellipse with non-orthogonal axes.</remarks>
 | |
|     [DebuggerDisplay("{Center} ellipse {AxisA}; {AxisB}")]
 | |
|     public struct QEllipse
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Ellipse center point.
 | |
|         /// </summary>
 | |
|         public QVec2 Center;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// First ellipse axis.
 | |
|         /// </summary>
 | |
|         public QVec2 AxisA;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Second ellipse axis.
 | |
|         /// </summary>
 | |
|         public QVec2 AxisB;
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A triangle.
 | |
|     /// </summary>
 | |
|     [DebuggerDisplay("{A} -- {B} -- {C}")]
 | |
|     public struct QTriangle
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// First vertex.
 | |
|         /// </summary>
 | |
|         public QVec2 A;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Second vertex.
 | |
|         /// </summary>
 | |
|         public QVec2 B;
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Third vertex.
 | |
|         /// </summary>
 | |
|         public QVec2 C;
 | |
|     }
 | |
| 
 | |
|     [DebuggerDisplay("[{M11} {M12} {M13} {M14}; {M21} {M22} {M23} {M24}; {M31} {M32} {M33} {M34}; {M41} {M42} {M43} {M44}]")]
 | |
|     public struct QMat4
 | |
|     {
 | |
|         public float M11, M21, M31, M41;
 | |
|         public float M12, M22, M32, M42;
 | |
|         public float M13, M23, M33, M43;
 | |
|         public float M14, M24, M34, M44;
 | |
| 
 | |
|         public static QMat4 Identity { get; } = new QMat4()
 | |
|         {
 | |
|             M11 = 1.0f,
 | |
|             M22 = 1.0f,
 | |
|             M33 = 1.0f,
 | |
|             M44 = 1.0f
 | |
|         };
 | |
| 
 | |
|         public static void Translation(out QMat4 mat, float x, float y, float z)
 | |
|         {
 | |
|             mat = Identity;
 | |
|             mat.M41 = x;
 | |
|             mat.M42 = y;
 | |
|             mat.M43 = z;
 | |
|         }
 | |
| 
 | |
|         public static void Scale(out QMat4 mat, float x, float y, float z)
 | |
|         {
 | |
|             mat = default;
 | |
|             mat.M11 = x;
 | |
|             mat.M22 = y;
 | |
|             mat.M33 = z;
 | |
|             mat.M44 = 1.0f;
 | |
|         }
 | |
| 
 | |
|         public static void Orthographic(out QMat4 mat, QRectangle bounds, float near = 1, float far = -1)
 | |
|         {
 | |
|             float a, b, c;
 | |
|             mat = default;
 | |
| 
 | |
|             a = 1.0f/(bounds.Right - bounds.Left);
 | |
|             b = 1.0f/(bounds.Top - bounds.Bottom);
 | |
|             c = 1.0f/(near - far);
 | |
| 
 | |
|             mat.M11 = 2 * a;
 | |
|             mat.M22 = 2 * b;
 | |
|             mat.M33 = 2 * c;
 | |
| 
 | |
|             mat.M41 = -a * (bounds.Left + bounds.Right);
 | |
|             mat.M42 = -b * (bounds.Top + bounds.Bottom);
 | |
|             mat.M43 = -c * (near + far);
 | |
|             mat.M44 = 1.0f;
 | |
|         }
 | |
|     }
 | |
| } |