Skip to content

Finalize Maths API #215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Maths/ConvertHelper/ConvertHelper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Superpower" Version="2.3.0" />
</ItemGroup>

</Project>
88 changes: 88 additions & 0 deletions src/Maths/ConvertHelper/Parser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#region

using Superpower;
using Superpower.Parsers;

#endregion

namespace ConvertHelper
{
internal class ArithmeticExpressionParser
{
private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Add = Operator
(ArithmeticExpressionToken.Plus, ExpressionType.Add);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Subtract = Operator
(ArithmeticExpressionToken.Minus, ExpressionType.Subtract);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Multiply = Operator
(ArithmeticExpressionToken.Times, ExpressionType.Multiply);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Divide = Operator
(ArithmeticExpressionToken.Divide, ExpressionType.Divide);

private static readonly TokenListParser<ArithmeticExpressionToken, ExpressionType> Comma = Operator
(ArithmeticExpressionToken.Comma, ExpressionType.Comma);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Variable = Token.EqualTo
(ArithmeticExpressionToken.None)
.Apply(Character.LetterOrDigit.Or(Character.EqualTo('.')).AtLeastOnce())
.Select(n => new string(n))
.Named("variable");

private static readonly TokenListParser<ArithmeticExpressionToken, string> Constant = Token.EqualTo
(ArithmeticExpressionToken.Number)
.Apply(Numerics.Decimal)
.Select(n => $"Scalar.As<T>({n.ToString()})")
.Named("constant")
.Or(Variable);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Factor =
(from lparen in Token.EqualTo
(ArithmeticExpressionToken.LParen)
from expr in Parse.Ref(() => Expr)
from rparen in Token.EqualTo(ArithmeticExpressionToken.RParen)
select $"({expr})").Or(Constant);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Operand = (from sign in Token.EqualTo
(ArithmeticExpressionToken.Minus)
from factor in Factor
select $"Scalar.Negate<T>({factor})").Or(Factor)
.Named("expression");

private static readonly TokenListParser<ArithmeticExpressionToken, string> Term = Parse.Chain
(Multiply.Or(Divide), Operand, AddParenthese);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Expr = Parse.Chain
(Add.Or(Subtract), Term, AddParenthese);

private static readonly TokenListParser<ArithmeticExpressionToken, string> Exprs = Parse.Chain
(Comma, Expr, AddParenthese);

public static readonly TokenListParser<ArithmeticExpressionToken, string> Transform = Exprs.AtEnd();

private static TokenListParser<ArithmeticExpressionToken, ExpressionType> Operator
(ArithmeticExpressionToken op, ExpressionType opType)
=> Token.EqualTo(op).Value(opType);

private static string AddParenthese(ExpressionType type, string left, string right)
=> type switch
{
ExpressionType.Add => $"Scalar.Add<T>({left}, {right})",
ExpressionType.Subtract => $"Scalar.Subtract<T>({left}, {right})",
ExpressionType.Multiply => $"Scalar.Multiply<T>({left}, {right})",
ExpressionType.Divide => $"Scalar.Divide<T>({left}, {right})",
ExpressionType.Comma => $"({left}), ({right})",
_ => $"Scalar.???<T>({left}, {right})"
};

private enum ExpressionType
{
Add,
Subtract,
Multiply,
Divide,
Comma
}
}
}
53 changes: 53 additions & 0 deletions src/Maths/ConvertHelper/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#region

using System;
using System.Text;

#endregion

namespace ConvertHelper
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine
("Hey! I'll try to convert any normal C# expression to a Silk.Net.Maths.Scalar using expression!");
Console.WriteLine("Simply paste your code below, and I'll translate line-by-line. I'll try to keep `,`");
Console.WriteLine("A single empty line will start translation");

var tokenizer = new ArithmeticExpressionTokenizer();

while (true)
{
var sb = new StringBuilder();
while (true)
{
var i = Console.ReadLine();
if (string.IsNullOrWhiteSpace(i))
{
break;
}

sb.AppendLine(i);
}

var result = tokenizer.TryTokenize(sb.ToString());
if (!result.HasValue)
{
Console.WriteLine(result.FormatErrorMessageFragment());
continue;
}

var transform = ArithmeticExpressionParser.Transform(result.Value);
if (!transform.HasValue)
{
Console.WriteLine(result.FormatErrorMessageFragment());
continue;
}

Console.WriteLine(transform.Value);
}
}
}
}
33 changes: 33 additions & 0 deletions src/Maths/ConvertHelper/Token.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#region

using Superpower.Display;

#endregion

namespace ConvertHelper
{
internal enum ArithmeticExpressionToken
{
None,

Number,

[Token(Category = "operator", Example = "+")]
Plus,

[Token(Category = "operator", Example = "-")]
Minus,

[Token(Category = "operator", Example = "*")]
Times,

[Token(Category = "operator", Example = "-")]
Divide,

[Token(Example = "(")] LParen,

[Token(Example = ")")] RParen,

[Token(Example = ",")] Comma
}
}
77 changes: 77 additions & 0 deletions src/Maths/ConvertHelper/Tokenizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#region

using System.Collections.Generic;
using Superpower;
using Superpower.Model;
using Superpower.Parsers;

#endregion

namespace ConvertHelper
{
internal class ArithmeticExpressionTokenizer : Tokenizer<ArithmeticExpressionToken>
{
private readonly Dictionary<char, ArithmeticExpressionToken> _operators =
new Dictionary<char, ArithmeticExpressionToken>
{
['+'] = ArithmeticExpressionToken.Plus,
['-'] = ArithmeticExpressionToken.Minus,
['*'] = ArithmeticExpressionToken.Times,
['/'] = ArithmeticExpressionToken.Divide,
['('] = ArithmeticExpressionToken.LParen,
[')'] = ArithmeticExpressionToken.RParen,
[','] = ArithmeticExpressionToken.Comma
};

protected override IEnumerable<Result<ArithmeticExpressionToken>> Tokenize(TextSpan span)
{
var next = SkipWhiteSpace(span);
if (!next.HasValue)
{
yield break;
}

do
{
ArithmeticExpressionToken charToken;

var ch = next.Value;
if (ch >= '0' && ch <= '9')
{
var integer = Numerics.Integer(next.Location);
next = integer.Remainder.ConsumeChar();
yield return Result.Value(ArithmeticExpressionToken.Number, integer.Location, integer.Remainder);
}
else if (_operators.TryGetValue(ch, out charToken))
{
yield return Result.Value(charToken, next.Location, next.Remainder);
next = next.Remainder.ConsumeChar();
}
else
{
var start = next.Location;
while (true)
{
var c = next.Location.ConsumeChar();
if (!c.HasValue || _operators.TryGetValue(c.Value, out _))
{
break;
}

if (char.IsWhiteSpace(c.Value))
{
break;
}

next = c.Remainder.ConsumeChar();
}

var location = start.Until(next.Location);
yield return Result.Value(ArithmeticExpressionToken.None, location, next.Location);
}

next = SkipWhiteSpace(next.Location);
} while (next.HasValue);
}
}
}
8 changes: 6 additions & 2 deletions src/Maths/Silk.NET.Maths/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using System;
#region

[assembly: CLSCompliant(true)]
using System;

#endregion

[assembly: CLSCompliant(true)]
76 changes: 31 additions & 45 deletions src/Maths/Silk.NET.Maths/Box2.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
// This file is part of Silk.NET.
//
// You may modify and distribute Silk.NET under the terms
// of the MIT license. See the LICENSE file for details.
#region

using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using static Silk.NET.Maths.Scalar;

#endregion

namespace Silk.NET.Maths
{
public readonly struct Box2<T> : IEquatable<Box2<T>>, IFormattable where T : unmanaged, IFormattable
{
public readonly Vector2<T> Min;
public readonly Vector2<T> Max;
public Vector2<T> Min { get; }
public Vector2<T> Max { get; }

public Box2(Vector2<T> min, Vector2<T> max)
{
Min = min;
Max = max;
}
public Box2(Vector2<T> min, Vector2<T> max) => (Min, Max) = (min, max);

public Box2(T minX, T minY, T maxX, T maxY) : this(new Vector2<T>(minX, minY), new Vector2<T>(maxX, maxY))
{
}
public Box2(T minX, T minY, T maxX, T maxY) : this(new Vector2<T>(minX, minY), new Vector2<T>(maxX, maxY)) { }

public Vector2<T> Size => Max - Min;

Expand All @@ -35,40 +29,32 @@ public Box2(Vector2<T> min, Vector2<T> max)

public Box2<T> WithMax(Vector2<T> max) => new Box2<T>(Min, max);

public bool Contains(Vector2<T> point, bool boundaryInclusive = false) =>
boundaryInclusive
? LargerEquals(point.X, Min.X)
&& LargerEquals(point.Y, Min.Y)
&& SmallerEquals(point.X, Max.X)
&& SmallerEquals(point.Y, Min.Y)
: Larger(point.X, Min.X)
&& Larger(point.Y, Min.Y)
&& Smaller(point.X, Max.X)
&& Smaller(point.Y, Min.Y);

public bool Contains(Box2<T> other, bool boundaryInclusive = false) =>
boundaryInclusive
? LargerEquals(other.Min.X, Min.X)
&& LargerEquals(other.Min.Y, Min.Y)
&& SmallerEquals(other.Max.X, Max.X)
&& SmallerEquals(other.Max.Y, Min.Y)
: Larger(other.Min.X, Min.X)
&& Larger(other.Min.Y, Min.Y)
&& Smaller(other.Max.X, Max.X)
&& Smaller(other.Max.Y, Min.Y);

public T DistanceToNearestEdge(Vector2<T> point) => Vector2<T>.ComponentMax
(Vector2<T>.Zero, Vector2<T>.ComponentMax(Min - point, point - Max))
.Length;
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public bool Contains(Vector2<T> point, bool boundaryInclusive = false)
=> boundaryInclusive
? LargerEquals(point.X, Min.X) && LargerEquals(point.Y, Min.Y) && SmallerEquals(point.X, Max.X) &&
SmallerEquals(point.Y, Min.Y)
: Larger(point.X, Min.X) && Larger(point.Y, Min.Y) && Smaller(point.X, Max.X) && Smaller
(point.Y, Min.Y);

public bool Contains(Box2<T> other, bool boundaryInclusive = false)
=> boundaryInclusive
? LargerEquals(other.Min.X, Min.X) && LargerEquals(other.Min.Y, Min.Y) && SmallerEquals
(other.Max.X, Max.X) && SmallerEquals(other.Max.Y, Min.Y)
: Larger(other.Min.X, Min.X) && Larger(other.Min.Y, Min.Y) && Smaller(other.Max.X, Max.X) && Smaller
(other.Max.Y, Min.Y);
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters

public T DistanceToNearestEdge(Vector2<T> point)
=> Vector2<T>.ComponentMax(Vector2<T>.Zero, Vector2<T>.ComponentMax(Min - point, point - Max)).Length;

public Box2<T> Translate(Vector2<T> distance) => new Box2<T>(Min + distance, Max + distance);

public Box2<T> Scale
(Vector2<T> scale, Vector2<T> anchor) => new Box2<T>
(anchor + (Min - anchor) * scale, anchor + (Max - anchor) * scale);
public Box2<T> Scale(Vector2<T> scale, Vector2<T> anchor)
=> new Box2<T>(anchor + (Min - anchor) * scale, anchor + (Max - anchor) * scale);

public Box2<T> Inflate
(Vector2<T> point) => new Box2<T>(Vector2<T>.ComponentMin(point, Min), Vector2<T>.ComponentMin(point, Max));
public Box2<T> Inflate(Vector2<T> point)
=> new Box2<T>(Vector2<T>.ComponentMin(point, Min), Vector2<T>.ComponentMin(point, Max));

public static bool operator ==(Box2<T> left, Box2<T> right) => left.Min == right.Min && left.Max == right.Max;

Expand All @@ -93,4 +79,4 @@ public string ToString(string? format, IFormatProvider? formatProvider)
return sb.ToString();
}
}
}
}
Loading