1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-24 21:48:35 +01:00

Compare commits

...

3 commits

9 changed files with 62 additions and 11 deletions

View file

@ -15,6 +15,7 @@ Additions
Improvements Improvements
- Exposed Camera's RoundPosition - Exposed Camera's RoundPosition
- Exposed the epsilon value used by Camera
### MLEM.Ui ### MLEM.Ui
Additions Additions
@ -23,6 +24,7 @@ Additions
Improvements Improvements
- Cache TokenizedString inner offsets for non-Left text alignments to improve performance - Cache TokenizedString inner offsets for non-Left text alignments to improve performance
- Exposed the epsilon value used by Element calculations
Fixes Fixes
- Fixed VerticalSpace height parameter being an integer - Fixed VerticalSpace height parameter being an integer

View file

@ -100,8 +100,7 @@ namespace MLEM.Data.Content {
continue; continue;
if (!type.IsSubclassOf(typeof(RawContentReader))) if (!type.IsSubclassOf(typeof(RawContentReader)))
continue; continue;
var inst = type.GetConstructor(Type.EmptyTypes).Invoke(null); ret.Add((RawContentReader) Activator.CreateInstance(type));
ret.Add((RawContentReader) inst);
} catch (Exception e) { } catch (Exception e) {
throw new NotSupportedException($"The type {type} cannot be constructed by a RawContentManager. Does it have a visible parameterless constructor?", e); throw new NotSupportedException($"The type {type} cannot be constructed by a RawContentManager. Does it have a visible parameterless constructor?", e);
} }

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
@ -322,8 +323,7 @@ namespace MLEM.Data {
} }
private static DynamicEnum Construct(Type type, string name, BigInteger value) { private static DynamicEnum Construct(Type type, string name, BigInteger value) {
var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] {typeof(string), typeof(BigInteger)}, null); return (DynamicEnum) Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] {name, value}, CultureInfo.InvariantCulture);
return (DynamicEnum) constructor.Invoke(new object[] {name, value});
} }
private class Storage { private class Storage {

View file

@ -13,7 +13,7 @@ namespace MLEM.Data.Json {
/// </summary> /// </summary>
public static readonly JsonConverter[] Converters = typeof(JsonConverters).Assembly.GetExportedTypes() public static readonly JsonConverter[] Converters = typeof(JsonConverters).Assembly.GetExportedTypes()
.Where(t => t.IsSubclassOf(typeof(JsonConverter)) && !t.IsGenericType) .Where(t => t.IsSubclassOf(typeof(JsonConverter)) && !t.IsGenericType)
.Select(t => t.GetConstructor(Type.EmptyTypes).Invoke(null)).Cast<JsonConverter>().ToArray(); .Select(Activator.CreateInstance).Cast<JsonConverter>().ToArray();
/// <summary> /// <summary>
/// Adds all of the <see cref="JsonConverter"/> objects that are part of MLEM.Data to the given <see cref="JsonSerializer"/> /// Adds all of the <see cref="JsonConverter"/> objects that are part of MLEM.Data to the given <see cref="JsonSerializer"/>

View file

@ -14,6 +14,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Logo.png</PackageIcon> <PackageIcon>Logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<NoWarn>NU1701</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -18,6 +18,12 @@ namespace MLEM.Ui.Elements {
/// </summary> /// </summary>
public abstract class Element : GenericDataHolder, IDisposable { public abstract class Element : GenericDataHolder, IDisposable {
/// <summary>
/// This field holds an epsilon value used in element <see cref="Size"/>, position and resulting <see cref="Area"/> calculations to mitigate floating point rounding inaccuracies.
/// If ui elements used are extremely small or extremely large, this value can be reduced or increased.
/// </summary>
public static float Epsilon = 0.01F;
/// <summary> /// <summary>
/// A list of all of this element's direct children. /// A list of all of this element's direct children.
/// Use <see cref="AddChild{T}"/> or <see cref="RemoveChild"/> to manipulate this list while calling all of the necessary callbacks. /// Use <see cref="AddChild{T}"/> or <see cref="RemoveChild"/> to manipulate this list while calling all of the necessary callbacks.
@ -607,9 +613,9 @@ namespace MLEM.Ui.Elements {
break; break;
case Anchor.AutoInline: case Anchor.AutoInline:
var newX = prevArea.Right + this.ScaledOffset.X; var newX = prevArea.Right + this.ScaledOffset.X;
// with awkward ui scale values, floating point rounding can cause an element that would usually be // with awkward ui scale values, floating point rounding can cause an element that would usually
// positioned correctly to be pushed into the next line due to a very small deviation, so we add 0.01 here // be positioned correctly to be pushed into the next line due to a very small deviation
if (newX + newSize.X <= parentArea.Right + 0.01F) { if (newX + newSize.X <= parentArea.Right + Epsilon) {
pos.X = newX; pos.X = newX;
pos.Y = prevArea.Y + this.ScaledOffset.Y; pos.Y = prevArea.Y + this.ScaledOffset.Y;
} else { } else {
@ -676,7 +682,7 @@ namespace MLEM.Ui.Elements {
} }
// we want to leave some leeway to prevent float rounding causing an infinite loop // we want to leave some leeway to prevent float rounding causing an infinite loop
if (!autoSize.Equals(this.UnscrolledArea.Size, 0.01F)) { if (!autoSize.Equals(this.UnscrolledArea.Size, Epsilon)) {
recursion++; recursion++;
if (recursion >= 16) { if (recursion >= 16) {
throw new ArithmeticException($"The area of {this} with root {this.Root.Name} has recursively updated too often. Does its child {foundChild} contain any conflicting auto-sizing settings?"); throw new ArithmeticException($"The area of {this} with root {this.Root.Name} has recursively updated too often. Does its child {foundChild} contain any conflicting auto-sizing settings?");

View file

@ -45,7 +45,7 @@ namespace MLEM.Ui.Elements {
// force current value to be clamped // force current value to be clamped
this.CurrentValue = this.CurrentValue; this.CurrentValue = this.CurrentValue;
// auto-hide if necessary // auto-hide if necessary
var shouldHide = this.maxValue <= 0.01F; var shouldHide = this.maxValue <= Epsilon;
if (this.AutoHideWhenEmpty && this.IsHidden != shouldHide) { if (this.AutoHideWhenEmpty && this.IsHidden != shouldHide) {
this.IsHidden = shouldHide; this.IsHidden = shouldHide;
this.OnAutoHide?.Invoke(this); this.OnAutoHide?.Invoke(this);

View file

@ -11,6 +11,12 @@ namespace MLEM.Cameras {
/// </summary> /// </summary>
public class Camera { public class Camera {
/// <summary>
/// This field holds an epsilon value used in some camera calculations to mitigate floating point rounding inaccuracies.
/// If camera <see cref="Position"/> or <see cref="Viewport"/> size are extremely small or extremely big, this value can be reduced or increased.
/// </summary>
public static float Epsilon = 0.01F;
/// <summary> /// <summary>
/// The top-left corner of the camera's viewport. /// The top-left corner of the camera's viewport.
/// <seealso cref="LookingPosition"/> /// <seealso cref="LookingPosition"/>
@ -158,7 +164,7 @@ namespace MLEM.Cameras {
if (this.Max.Y > max.Y) if (this.Max.Y > max.Y)
this.Max = new Vector2(this.Max.X, max.Y); this.Max = new Vector2(this.Max.X, max.Y);
} }
return !this.Position.Equals(lastPos, 0.001F); return !this.Position.Equals(lastPos, Epsilon);
} }
/// <summary> /// <summary>

View file

@ -1,12 +1,15 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Numerics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using MLEM.Data; using MLEM.Data;
using MLEM.Data.Json; using MLEM.Data.Json;
using MLEM.Misc; using MLEM.Misc;
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using static MLEM.Data.DynamicEnum;
using Vector2 = Microsoft.Xna.Framework.Vector2;
namespace Tests { namespace Tests {
public class DataTests { public class DataTests {
@ -62,6 +65,33 @@ namespace Tests {
TestContext.WriteLine($"DeepCopy took {stopwatch.Elapsed.TotalMilliseconds / count * 1000000}ns on average"); TestContext.WriteLine($"DeepCopy took {stopwatch.Elapsed.TotalMilliseconds / count * 1000000}ns on average");
} }
[Test]
public void TestDynamicEnum() {
var flags = new TestEnum[100];
for (var i = 0; i < flags.Length; i++)
flags[i] = AddFlag<TestEnum>("Flag" + i);
Assert.AreEqual(GetValue(flags[7]), BigInteger.One << 7);
Assert.AreEqual(GetEnumValue<TestEnum>(BigInteger.One << 75), flags[75]);
Assert.AreEqual(GetValue(Or(flags[2], flags[17])), (BigInteger.One << 2) | (BigInteger.One << 17));
Assert.AreEqual(GetValue(And(flags[2], flags[3])), BigInteger.Zero);
Assert.AreEqual(And(Or(flags[24], flags[52]), Or(flags[52], flags[75])), flags[52]);
Assert.AreEqual(Xor(Or(flags[85], flags[73]), flags[73]), flags[85]);
Assert.AreEqual(Xor(Or(flags[85], Or(flags[73], flags[12])), flags[73]), Or(flags[85], flags[12]));
Assert.AreEqual(GetValue(Neg(flags[74])), ~(BigInteger.One << 74));
Assert.AreEqual(Or(flags[24], flags[52]).HasFlag(flags[24]), true);
Assert.AreEqual(Or(flags[24], flags[52]).HasAnyFlag(flags[24]), true);
Assert.AreEqual(Or(flags[24], flags[52]).HasFlag(Or(flags[24], flags[26])), false);
Assert.AreEqual(Or(flags[24], flags[52]).HasAnyFlag(Or(flags[24], flags[26])), true);
Assert.AreEqual(Parse<TestEnum>("Flag24"), flags[24]);
Assert.AreEqual(Parse<TestEnum>("Flag24 | Flag43"), Or(flags[24], flags[43]));
Assert.AreEqual(flags[24].ToString(), "Flag24");
Assert.AreEqual(Or(flags[24], flags[43]).ToString(), "Flag24 | Flag43");
}
private class TestObject { private class TestObject {
public Vector2 Vec; public Vector2 Vec;
@ -86,5 +116,12 @@ namespace Tests {
} }
private class TestEnum : DynamicEnum {
public TestEnum(string name, BigInteger value) : base(name, value) {
}
}
} }
} }