From 6c479e9fd63dceb97f0c73b9f2cd9eb4e02c17fb Mon Sep 17 00:00:00 2001 From: Lelebees Date: Thu, 21 May 2026 10:53:09 +0200 Subject: [PATCH 1/4] Fix docs, add new functions to Sprites.cs to replace lost group functionality --- .../readme.md | 101 +++++++++--------- .../src/sprite/Sprites.cs | 49 +++++++++ 2 files changed, 98 insertions(+), 52 deletions(-) diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/readme.md b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/readme.md index 1bb8f25..685f79b 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/readme.md +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/readme.md @@ -9,56 +9,37 @@ An API wrapper that supports composing new sprites from multiple existing ones. ## Usage You can instantiate regular sprites using the helper methods of the `Sprites` class. These return a builder (except for -`Sprites.Group()`, which returns a group directly) that can be used to further configure the initial values of the +`Sprites.Compose()`, which returns a composite sprite directly) that can be used to further configure the initial values +of the sprite. All sprites and sprite groups implement the `Sprite` interface, which you can use to `Translate()`, `Rotate()`, or `Scale()` a sprite after instantiation, among other things. Individual Sprite types may have additional properties and -methods available, like `TextureSprite`'s `Mirror()` functions. - -See the [demo project](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs) for a detailed example. - -### Grouping Sprites - -You can group sprites by calling `Sprites.Group()` or creating a `new SimpleSpriteGroup()`. - -If this implementation does -not fit your needs, you can extend the `SpriteGroup` abstract class. If you do, you will be required to implement the -`Clone()` and `GetChildren()` methods. - -**Avoid creating a new list in `GetChildren()`**, as the function is invoked -whenever a transformation is applied to the group. I recommend using the following example as a guide: - -```csharp -public class CustomSpriteGroup : SpriteGroup -{ - private readonly List backingList; - - public Sprite UnspecifiedSprite - { - get { return backingList[0]; } - set { backingList[0] = value; } - } - - public TextureSprite SpecificSprite - { - get { return (TextureSprite) backingList[1]; } - set { backingList[1] = value; } - } - - public CustomSpriteGroup(Sprite unspecifiedSprite, TextureSprite specificSprite) - { - this.backingList = new List { unspecifiedSprite, specificSprite }; - } - - public override Sprite Clone() => new CustomSpriteGroup(backingList.Select(sprite => sprite.Clone()).ToList()); - - protected override List GetChildren() => backingList; -} -``` - -If, for some reason, the abstract `SpriteGroup` class also does not suit your needs, you can implement the `Sprite` -interface directly, though if you feel the need to do so, there may be a structural problem with your program. +methods available. + +See +the [demo project](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs) +for a detailed example. + +### Composing Sprites + +You can compose a new sprite by calling `Sprites.Compose()` or creating a `new CompositeSprite()`. This Composite sprite +will act just like a normal sprite, even though it is made up of multiple child sprites. + +Once you have created a composite sprite, you cannot add or remove child sprites. If you need to perform operations on +multiple sprites without creating a composite sprite, use the dedicated `Sprites` methods which are clarified below. + +If these options do not +not fit your needs, you can extend the `CompositeSprite` class, or implement the `Sprite` interface yourself. + +You should avoid composing a sprite that has both `TextSprite` objects and `TextureSprite` objects as they behave +differently and can cause unexpected behavior when grouped together. + +### Performing Transformations On Groups + +Sometimes you don't want to compose a sprite, but you do want to apply the same transformation to multiple sprites. +In this case the `Sprites` abstract class has `Translate()`, `Scale()` and `Rotate()` functions available that can +transform multiple sprites at once. ### Anchors @@ -69,22 +50,38 @@ an `Anchor` will also scale the distance to the anchor point. ### Displaying Sprites -In order to draw your composed sprites to an LCD screen, you'll need to call the `Sprite.AsRenderable()` method, which will +In order to draw your composed sprites to an LCD screen, you'll need to call the `Sprite.AsRenderable()` method, which +will return an array of `MySprite` objects that your sprite consists of. You can draw these to the screen in one go using the `MySpriteDrawFrame.AddRange()` method. `AsRenderable()` takes an optional `RectangleF viewport` as parameter. Supplying this will move the sprites so that (0,0) is the center of the viewport. ## Legal + `Copyright (c) 2026 Lelebees` -This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. -You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . +You should have received a copy of the GNU Lesser General Public License along with this program. If not, +see . ### License -You can find a copy of the [GNU General Public License](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/COPYING) and [GNU Lesser General Public License](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/COPYING.LESSER) next to this source code in COPYING and COPYING.LESSER respectively. + +You can find a copy of +the [GNU General Public License](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/COPYING) +and [GNU Lesser General Public License](https://github.com/malforge/mdk2-packages/blob/main/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/COPYING.LESSER) +next to this source code in COPYING and COPYING.LESSER respectively. ### Reaching out -You can reach me as @lelebees on Discord, or through the project's [Github Repository](https://github.com/Lelebees/mdk2-packages-sprite-compositor). Please note while reaching out on Discord that I generally do not accept random friend requests. @Mention me in the [programmable block channel](https://discord.com/channels/125011928711036928/216219467959500800) of the Keen Software House Discord Server to get a hold of me. \ No newline at end of file + +You can reach me as @lelebees on Discord, or through the +project's [GitHub Repository](https://github.com/Lelebees/mdk2-packages-sprite-compositor). Please note while reaching +out on Discord that I generally do not accept random friend requests. @Mention me in +the [programmable block channel](https://discord.com/channels/125011928711036928/216219467959500800) of the Keen +Software House Discord Server to get a hold of me. \ No newline at end of file diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/Sprites.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/Sprites.cs index 9b72a7c..7896056 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/Sprites.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/Sprites.cs @@ -170,5 +170,54 @@ public static Sprite MirroredHorizontal(Sprite sprite, Anchor anchor = null) clone.Scale(new Vector2(-1, 1), anchor); return clone; } + + /// + /// Translate a group of sprites with the given vector + /// + /// the offset to translate the sprites by + /// the sprites to translate + public static void Translate(Vector2 vector, params Sprite[] sprites) + { + foreach (var sprite in sprites) sprite.Translate(vector); + } + + /// + /// Translate a group of sprites with the x and y offset + /// + /// the x-offset to translate the sprites by + /// the y-offset to translate the sprites by + /// the sprites to translate + public static void Translate(float x, float y, params Sprite[] sprites) => Translate(new Vector2(x, y), sprites); + + /// + /// Rotate a group of sprites with the given angle. Rotates in place unless an anchor is given. + /// + /// The angle to rotate the sprites by + /// Optional anchor to rotate around + /// the sprites to rotate + public static void Rotate(Angle angle, Anchor anchor = null, params Sprite[] sprites) + { + foreach (var sprite in sprites) sprite.Rotate(angle, anchor); + } + + /// + /// Scale a group of sprites with the given scalars. X and Y can scale separately. Scales in place unless an anchor is given. + /// + /// The amount to scale x and y by. + /// If passed, scales a sprite's distance to this point in addition to the sprite itself. + /// The sprites to scale + public static void Scale(Vector2 scalar, Anchor anchor = null, params Sprite[] sprites) + { + foreach (var sprite in sprites) sprite.Scale(scalar, anchor); + } + + /// + /// Scale a group of sprites with the given scalar. Scales in place unless an anchor is given. + /// + /// the amount to scale the sprites by + /// If passed, scales a sprite's distance to this point in addition to the sprite itself. + /// The sprites to scale + public static void Scale(float scalar, Anchor anchor = null, params Sprite[] sprites) => + Scale(new Vector2(scalar, scalar), anchor, sprites); } } \ No newline at end of file From 7db2ec000fc240dd861acfdfadcb710cdda60ca2 Mon Sep 17 00:00:00 2001 From: Lelebees Date: Thu, 21 May 2026 13:11:02 +0200 Subject: [PATCH 2/4] Updated MDK & fixed floating point issue indefinite rotations no longer build up floating point error over time --- .../src/sprite/atoms/TextureSprite.cs | 5 +++-- .../SpriteCompositor.Demo/Program.cs | 3 +-- .../SpriteCompositor.Demo/SpriteCompositor.Demo.csproj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs index 993ff03..3a475f9 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs @@ -11,6 +11,7 @@ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR You should have received a copy of the GNU Lesser General Public License along with Sprite Compositor. If not, see . */ +using System; using VRage.Game.GUI.TextPanel; using VRageMath; @@ -51,12 +52,12 @@ public override void Scale(Vector2 scalar, Anchor anchor = null) public override void Rotate(Angle angle, Anchor positionAnchor = null) { - Sprite.RotationOrScale += (float)angle.Radians; + Sprite.RotationOrScale = (float)((Sprite.RotationOrScale + angle.Radians) % 2 * Math.PI); base.Rotate(angle, positionAnchor); } public override Sprite Clone() => new TextureSprite(Sprite); - + public class TextureSpriteBuilder { diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs index 8b334f3..d214f88 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/Program.cs @@ -79,8 +79,7 @@ This will therefore not create slanted text! */ // You can also set an initial scale for text using the builder. // However, here we want to scale the distance the text has to the sun, so we supply the sun as the anchor for the scale operation. textLayer.Scale(1.5f, sunSprite); - // Any new operations will be applied to the newly grouped sprites as well, but previous operations will not be applied. - sunSprite.Scale(2f); + sunSprite.Scale(2); // It's also possible to scale X and Y separately. } diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/SpriteCompositor.Demo.csproj b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/SpriteCompositor.Demo.csproj index d5606e6..68cbd7e 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/SpriteCompositor.Demo.csproj +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Demo/SpriteCompositor.Demo.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 32f551e112c40e8d547cbefbb540f65b3fe76d68 Mon Sep 17 00:00:00 2001 From: Lelebees Date: Thu, 21 May 2026 13:33:17 +0200 Subject: [PATCH 3/4] Potential floating point error cascade fix Potentially fix an issue where rotations would cascade floating point errors and build up an inaccuracy over time --- .../src/sprite/atoms/TextureSprite.cs | 2 +- .../src/sprite/molecules/CompositeSprite.cs | 2 +- .../src/sprite/atoms/TextureSpriteTest.cs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs index 3a475f9..c3782e0 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/atoms/TextureSprite.cs @@ -52,7 +52,7 @@ public override void Scale(Vector2 scalar, Anchor anchor = null) public override void Rotate(Angle angle, Anchor positionAnchor = null) { - Sprite.RotationOrScale = (float)((Sprite.RotationOrScale + angle.Radians) % 2 * Math.PI); + Sprite.RotationOrScale = (float)((Sprite.RotationOrScale + angle.Radians) % (2 * Math.PI)); base.Rotate(angle, positionAnchor); } diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/molecules/CompositeSprite.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/molecules/CompositeSprite.cs index 5a79024..c401f6e 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/molecules/CompositeSprite.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/src/sprite/molecules/CompositeSprite.cs @@ -107,7 +107,7 @@ public virtual void Rotate(Angle angle, Anchor positionAnchor = null) { if (Children[index].Type != SpriteType.TEXT) { - Children[index].RotationOrScale += (float)angle.Radians; + Children[index].RotationOrScale =(float)((Children[index].RotationOrScale + angle.Radians) % (2 * Math.PI)); } if (anchor == Children[index].Position) continue; diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Test/src/sprite/atoms/TextureSpriteTest.cs b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Test/src/sprite/atoms/TextureSpriteTest.cs index 51ccd38..d5ea2a8 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Test/src/sprite/atoms/TextureSpriteTest.cs +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/SpriteCompositor.Test/src/sprite/atoms/TextureSpriteTest.cs @@ -34,10 +34,8 @@ public void ArbitraryRotationsSetCorrectRotation(params double[] radians) { sprite.Rotate(angle); } - - // Note here (and this is not obvious) that unlike the angle struct, a sprite's rotation value can grow indefinitely - // This means that values above and below 2 * Math.PI are possible. The test keeps this in mind. - var totalAngle = angles.Sum(angle => angle.Radians); + + var totalAngle = angles.Sum(angle => angle.Radians) % (2 * Math.PI); Assert.That(sprite.Rotation, Is.EqualTo(totalAngle).Within(Precision)); } From b964deef7e6e09d5e1703ee8e32a1c51672e9b75 Mon Sep 17 00:00:00 2001 From: Lelebees Date: Thu, 21 May 2026 14:19:00 +0200 Subject: [PATCH 4/4] Add version and release notes --- .../Lelebees.MdkScriptMixin.SpriteCompositor/_releasenotes | 6 +++++- .../Lelebees.MdkScriptMixin.SpriteCompositor/_version | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_releasenotes b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_releasenotes index 2fdc642..2557610 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_releasenotes +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_releasenotes @@ -15,4 +15,8 @@ Further reduced runtime memory allocation when rendering drawables by replacing Sprite groups with Composite Sprites. Moved Mirror functionality to the Sprites abstract class. Added Repeat- methods to Sprites class for Scale and Translate respectively. - Break previous behaviour where you could edit a Sprite Group after it's instantiation. + Break previous behaviour where you could edit a Sprite Group after it's instantiation. +- 2.1.0 + Added transform functions to Sprites that replace lost Sprite Group functionality + Fixed readme being instructions for an older version of the mixin. + Fixed a floating point error accumulation bug when rotating sprites diff --git a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_version b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_version index 359a5b9..50aea0e 100644 --- a/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_version +++ b/libraries/Lelebees.MdkScriptMixin.SpriteCompositor/Lelebees.MdkScriptMixin.SpriteCompositor/_version @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.1.0 \ No newline at end of file