From 4d6e3b82aa5403f22ea85af95aa74509c22d50b2 Mon Sep 17 00:00:00 2001 From: Jonathan Keegan Date: Sat, 12 Nov 2022 19:27:14 -0500 Subject: [PATCH 01/26] Add Picturator --- .../Tools/SlideratorStuff/SliderPicturator.cs | 276 ++++++++++++ Mapping_Tools/MainWindow.xaml.cs | 15 +- Mapping_Tools/Mapping_Tools.csproj | 1 + Mapping_Tools/Viewmodels/CommandHandler.cs | 46 ++ .../Viewmodels/SliderPicturatorVM.cs | 408 ++++++++++++++++++ .../SliderPicturatorView.xaml | 256 +++++++++++ .../SliderPicturatorView.xaml.cs | 279 ++++++++++++ 7 files changed, 1280 insertions(+), 1 deletion(-) create mode 100644 Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs create mode 100644 Mapping_Tools/Viewmodels/CommandHandler.cs create mode 100644 Mapping_Tools/Viewmodels/SliderPicturatorVM.cs create mode 100644 Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml create mode 100644 Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs diff --git a/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs new file mode 100644 index 00000000..f46d1328 --- /dev/null +++ b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs @@ -0,0 +1,276 @@ +using Mapping_Tools.Classes.BeatmapHelper; +using Mapping_Tools.Classes.MathUtil; +using System; +using System.Linq; +using System.Drawing; +using System.Collections.Generic; + +namespace Mapping_Tools.Classes.Tools.SlideratorStuff +{ + public static class SliderPicturator + { + + private const double LIGHTEN_AMOUNT = 0.5; + private const double DARKEN_AMOUNT = 0.1; + private const byte ALPHA = 100; + public static int SNAPTOL => (int)Math.Pow(2, 5) * 3; + private static Color getOpaqueColor(Color top, Color bottom) + { + double GAMMA = 1; + double topOpacity = top.A / 255.0; + double bottomOpacity = bottom.A / 255.0; + double totOpacity = topOpacity + bottomOpacity * (1 - topOpacity); + return Color.FromArgb(255, + (byte)Math.Round(Math.Pow((Math.Pow(bottom.R, GAMMA) * bottomOpacity * (1 - topOpacity) + Math.Pow(top.R, GAMMA) * topOpacity) / totOpacity, 1 / GAMMA)), + (byte)Math.Round(Math.Pow((Math.Pow(bottom.G, GAMMA) * bottomOpacity * (1 - topOpacity) + Math.Pow(top.G, GAMMA) * topOpacity) / totOpacity, 1 / GAMMA)), + (byte)Math.Round(Math.Pow((Math.Pow(bottom.B, GAMMA) * bottomOpacity * (1 - topOpacity) + Math.Pow(top.B, GAMMA) * topOpacity) / totOpacity, 1 / GAMMA))); + } + public static Bitmap Recolor(Bitmap img, Color sliderColor, Color sliderBorder, Color backgroundColor, bool BLACK_OFF = false, bool BORDER_OFF = false, bool OPAQUE_OFF = false, bool R = true, bool G = true, bool B = true) + { + Color innerColor = Color.FromArgb(ALPHA, + (byte)Math.Min(255, sliderColor.R * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT), + (byte)Math.Min(255, sliderColor.G * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT), + (byte)Math.Min(255, sliderColor.B * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT)); + Color outerColor = Color.FromArgb(ALPHA, + (byte)Math.Min(255, sliderColor.R / (1 + DARKEN_AMOUNT)), + (byte)Math.Min(255, sliderColor.G / (1 + DARKEN_AMOUNT)), + (byte)Math.Min(255, sliderColor.B / (1 + DARKEN_AMOUNT))); + + Color opaqueIC = getOpaqueColor(innerColor, backgroundColor); + Color opaqueOC = getOpaqueColor(outerColor, backgroundColor); + + Vector3 projVec = new Vector3(opaqueIC.R - opaqueOC.R, opaqueIC.G - opaqueOC.G, opaqueIC.B - opaqueOC.B); + double projVecLen = projVec.Length; + Vector3 opaqueOCVec = new Vector3(opaqueOC.R, opaqueOC.G, opaqueOC.B); + Vector3 opaqueICVec = new Vector3(opaqueIC.R, opaqueIC.G, opaqueIC.B); + Vector3 sBColVec = new Vector3(sliderBorder.R, sliderBorder.G, sliderBorder.B); + + Color pixel; + Vector3 colorVec, proj, closestGradientVec, usedColor; + double gradientDist, borderDist, blackDist; + Bitmap ret = (Bitmap)img.Clone(); + for (int i = 0; i < img.Width; i++) { + for (int j = 0; j < img.Height; j++) { + pixel = img.GetPixel(i, j); + colorVec = new Vector3(R ? pixel.R : 0, G ? pixel.G : 0, B ? pixel.B : 0); + proj = Vector3.Dot(colorVec - opaqueOCVec, projVec) / Vector3.Dot(projVec, projVec) * projVec + opaqueOCVec; + if (proj.X < opaqueOCVec.X) { + closestGradientVec = opaqueOCVec; + } + else if (proj.X > opaqueICVec.X) { + closestGradientVec = opaqueICVec; + } + else { + closestGradientVec = proj; + } + gradientDist = (colorVec - closestGradientVec).LengthSquared; + borderDist = (colorVec - sBColVec).LengthSquared; + blackDist = colorVec.LengthSquared; + // Test if border color would be better + if (BORDER_OFF || gradientDist < borderDist) { + // Test if black would be better + if (!BLACK_OFF && blackDist < gradientDist) { + ret.SetPixel(i, j, Color.Black); + } + else { + usedColor = Math.Round(101 * Math.Clamp(1 - (closestGradientVec - opaqueOCVec).Length / projVecLen, 0, 1)) / 128 * projVec + opaqueOCVec; + ret.SetPixel(i, j, Color.FromArgb((int)Math.Round(usedColor[0]), (int)Math.Round(usedColor[1]), (int)Math.Round(usedColor[2]))); + } + } + else { + // Test if black would be better + if (!BLACK_OFF && blackDist < borderDist) { + ret.SetPixel(i, j, Color.Black); + } + else { + ret.SetPixel(i, j, sliderBorder); + } + } + } + } + return ret; + } + + // TODO: use color opacity per pixel, pass in background color, calculate opaqueIC and opaqueOC here + public static List Picturate(Bitmap img, Color sliderColor, Color sliderBorder, Color backgroundColor, double circleSize, Vector2 startPos, Vector2 startPosPic, double resY = 1080, long GPU = 16384, bool BLACK_OFF = false, bool BORDER_OFF = false, bool OPAQUE_OFF = false, bool R = true, bool G = true, bool B = true) + { + Color innerColor = Color.FromArgb(ALPHA, + (byte) Math.Min(255, sliderColor.R * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT), + (byte) Math.Min(255, sliderColor.G * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT), + (byte) Math.Min(255, sliderColor.B * (1 + 0.5 * LIGHTEN_AMOUNT) + 255 * LIGHTEN_AMOUNT)); + Color outerColor = Color.FromArgb(ALPHA, + (byte) Math.Min(255, sliderColor.R / (1 + DARKEN_AMOUNT)), + (byte) Math.Min(255, sliderColor.G / (1 + DARKEN_AMOUNT)), + (byte) Math.Min(255, sliderColor.B / (1 + DARKEN_AMOUNT))); + + Color opaqueIC = getOpaqueColor(innerColor, backgroundColor); + Color opaqueOC = getOpaqueColor(outerColor, backgroundColor); + + // startPos, startPosPic are in osupx + startPos.Round(); + startPosPic.Round(); + const int OSUPX_BETWEEN_ROWS = 240; + double objectRadius = 1.00041 * (54.4 - 4.48 * circleSize); + + Vector3 projVec = new Vector3(opaqueIC.R - opaqueOC.R, opaqueIC.G - opaqueOC.G, opaqueIC.B - opaqueOC.B); + double projVecLen = projVec.Length; + Vector3 opaqueOCVec = new Vector3(opaqueOC.R, opaqueOC.G, opaqueOC.B); + Vector3 opaqueICVec = new Vector3(opaqueIC.R, opaqueIC.G, opaqueIC.B); + Vector3 sBColVec = new Vector3(sliderBorder.R, sliderBorder.G, sliderBorder.B); + + double[,] pixDist = new double[img.Width, img.Height]; + Color pixel; + Vector3 colorVec, proj, closestGradientVec; + double gradientDist, borderDist, blackDist; + for (int i = 0; i < pixDist.GetLength(0); i++) { + for (int j = 0; j < pixDist.GetLength(1); j++) { + pixel = img.GetPixel(i, j); + colorVec = new Vector3(R ? pixel.R : 0, G ? pixel.G : 0, B ? pixel.B : 0); + proj = Vector3.Dot(colorVec - opaqueOCVec, projVec) / Vector3.Dot(projVec, projVec) * projVec + opaqueOCVec; + if (proj.X < opaqueOCVec.X) { + closestGradientVec = opaqueOCVec; + } + else if (proj.X > opaqueICVec.X) { + closestGradientVec = opaqueICVec; + } + else { + closestGradientVec = proj; + } + gradientDist = (colorVec - closestGradientVec).LengthSquared; + borderDist = (colorVec - sBColVec).LengthSquared; + blackDist = colorVec.LengthSquared; + // Test if border color would be better + if (BORDER_OFF || gradientDist < borderDist) { + // Test if black would be better + if (!BLACK_OFF && blackDist < gradientDist) { + pixDist[i, j] = 1; + } + else { + pixDist[i, j] = Math.Round(101 * Math.Clamp(1 - (closestGradientVec - opaqueOCVec).Length / projVecLen, 0, 1)) / 128; + } + } + else { + // Test if black would be better + if (!BLACK_OFF && blackDist < borderDist) { + pixDist[i, j] = 1; + } + else { + pixDist[i, j] = 111.0 / 128; + } + } + + } + } + + Vector2 topLeftOsuPxImage = new Vector2(-64, -72); // (16+20n, 8+20m) for matching editor to gameplay. Further than cs0 slider placed at (0,0)'s bounding box so we should be set. + Vector2 startSliderCoordinate = startPos; + // For now we will ignore the fact that this may interfere with the sample points + Vector2 topLeftOsuPxSlider = new Vector2(Math.Ceiling(objectRadius * 1.15)) + topLeftOsuPxImage; + Vector2 bottomRightOsuPxSlider = new Vector2(Math.Floor(OSUPX_BETWEEN_ROWS * GPU - 1.15 * objectRadius)) + topLeftOsuPxImage; + // To get screenpx from osupx topLeftOsuPxImage to osupx startPosPic we do the following: + // the game window is 480 osupx tall and resY-16 screenpx tall, so the ratio is (resY-16)/480 screenpx per osupx. + startPosPic -= topLeftOsuPxImage; + startPosPic *= (resY - 16) / 480; + startPosPic.Round(); + Vector2 imageStartOsuPx = topLeftOsuPxImage + OSUPX_BETWEEN_ROWS * startPosPic; + int columnStartCoordinate, columnEndCoordinate, columnStartOffset, relativeStartX, relativeStartY, absoluteStartX, absoluteStartY; + int leftToRight = -1; + double segmentSlope; + // In the below loop, gradientDist means something completely different from what it means in the above loop. Here, it is being used to mean the distance in the gradient between two or more points that are evenly distributed along the slider body + List sliderPath = new List(); + sliderPath.Add(startSliderCoordinate); + sliderPath.Add(new Vector2(startSliderCoordinate.X, topLeftOsuPxSlider.Y)); + sliderPath.Add(new Vector2(bottomRightOsuPxSlider.X, topLeftOsuPxSlider.Y)); + sliderPath.Add(bottomRightOsuPxSlider); + sliderPath.Add(new Vector2(bottomRightOsuPxSlider.X, topLeftOsuPxSlider.Y)); + sliderPath.Add(topLeftOsuPxSlider); + // Move to the start of the image, avoiding sample points (could be done better) + if (startPosPic.LengthSquared > 0) { + sliderPath.Add(new Vector2(topLeftOsuPxSlider.X, imageStartOsuPx.Y)); + sliderPath.Add(imageStartOsuPx); + } + absoluteStartX = 0; + columnStartOffset = 0; + absoluteStartY = 0; + gradientDist = 0; + for (int i = 0; i < img.Height; i++) { + leftToRight = -leftToRight; + columnStartCoordinate = (leftToRight == 1) ? 0 : (img.Width - 1); + columnEndCoordinate = columnStartCoordinate; + while ((leftToRight == 1) ? (columnStartCoordinate < img.Width) : (columnStartCoordinate >= 0)) { + // Look for gradients + columnStartOffset = 0; + gradientDist = 0; + if (0 <= columnStartCoordinate + leftToRight && columnStartCoordinate + leftToRight < img.Width) { + gradientDist = pixDist[columnStartCoordinate + leftToRight, i] - pixDist[columnStartCoordinate, i]; + columnStartOffset += leftToRight; + while (0 <= columnStartCoordinate + columnStartOffset + leftToRight && columnStartCoordinate + columnStartOffset + leftToRight < img.Width + && pixDist[columnStartCoordinate + columnStartOffset + leftToRight, i] - pixDist[columnStartCoordinate + columnStartOffset, i] == gradientDist) { + columnStartOffset += leftToRight; + } + } + columnEndCoordinate = columnStartCoordinate + columnStartOffset; + // First handle the case if columnStartCoordinate = columnEndCoordinate + // I belive this is being handled in the below case by simply setting gradientDist = 0 + + // Otherwise: + // Want to optimize gradientDist. We can control startPoint, but it should be between 55 and 65 away from columnStartCoordinate*OSUPX_BETWEEN_ROWS to avoid interfering + // 1. How close can we get to getting the actual slope? We want the slope to be gradientDist/OSUPX_BETWEEN_ROWS + // Suppose OSUPX_BETWEEN_ROWS*(columnStartCoordinate, i)+(OSUPX_BETWEEN_ROWS/2, OSUPX_BETWEEN_ROWS/2) is at (0,0). We need a radius of 55 around every sample point to avoid interfering. Given an x coordinate, the list of valid y coordinates are those such that + // need y such that x^2+y^2>55^2 but also (x+OSUPX_BETWEEN_ROWS)^2+y^2>55^2. To fix some problems on the edges we limit ourselves such that x>-OSUPX_BETWEEN_ROWS/2+55 and y>-OSUPX_BETWEEN_ROWS/2+55, and arbitrarily we choose y<0 since the region is symmetric. + // x>0 or x<0 depends on leftToRight. + + // A "best rational approximation" algorithm is not very functional here because the denominator needs to be in a specific range, + // and there's no guarantee that the best rational approximation will be a factor of a number in the range. Instead, we impose a stronger restriction, and just say that + // the slope's denominator is going to have a fixed size. The starting x coordinate will be at ((-55)+(-OSUPX_BETWEEN_ROWS/2+55))/2 = -OSUPX_BETWEEN_ROWS/4 relative to the sample point (i, columnStartCoordinate), + // and putting the sample point (i, columnEndCoordinate) at (0,0), the ending x coordinate will be at ((55)+(OSUPX_BETWEEN_ROWS/2-55))/2 = OSUPX_BETWEEN_ROWS/4. + // This means that the x-length of the slider segment is columnStartOffset*OSUPX_BETWEEN_ROWS+OSUPX_BETWEEN_ROWS/2 = (columnStartOffset+1/2)*OSUPX_BETWEEN_ROWS. + + // Therefore the height is given by round(gradientDist/OSUPX_BETWEEN_ROWS * (columnStartOffset*OSUPX_BETWEEN_ROWS + OSUPX_BETWEEN_ROWS/2)) = round(gradientDist*(columnStartOffset+1/2)) + + // We get the starting location by calculating a linear regression with fixed slope. At x=OSUPX_BETWEEN_ROWS*(columnStartOffset+j), we want y=pixDist[columnStartCoordinate+j, i]*objectRadius, for all j in [0, columnStartOffset]\cap Z. + // Using https://www.mathworks.com/matlabcentral/answers/67434-how-can-i-do-a-linear-fit-with-forced-slope, we get the y-intercept as: + // mean([pixDist[columnStartCoordinate+j, i]*objectRadius - round(gradientDist*(columnStartOffset+1/2))/((columnStartOffset+1/2)*OSUPX_BETWEEN_ROWS)*j*OSUPX_BETWEEN_ROWS for j in range(0, columnStartOffset+1)]) + // Writing that a bit more succinctly, + // mean([pixDist[columnStartCoordinate+j, i]*objectRadius - round(gradientDist*(columnStartOffset+1/2))/(columnStartOffset+1/2)*j for j in range(0, columnStartOffset+1)]) + // In fact, we can simplify this further by separating the two terms. + // mean([pixDist[columnStartCoordinate+j, i]*objectRadius for j in range(0, columnStartOffset+1)]) = objectRadius*mean([pixDist[columnStartCoordinate, i] + gradientDist*j for j in range(0, columnStartOffset+1)]) = objectRadius*(pixDist[columnStartCoordinate, i] + gradientDist*(columnStartOffset+1)/2) + // mean([round(gradientDist*(columnStartOffset+1/2))/(columnStartOffset+1/2)*j for j in range(0, columnStartOffset+1)]) = round(gradientDist*(columnStartOffset+1/2))/(columnStartOffset+1/2)*(columnStartOffset+1)/2 + // Therefore we have the y-intercept as objectRadius*(pixDist[columnStartCoordinate, i] + gradientDist*(columnStartOffset+1)/2)-round(gradientDist*(columnStartOffset+1/2))/(columnStartOffset+1/2)*(columnStartOffset+1)/2 + + // Actually, the vertical distance to the color we want (in units of the object radius) is affected by the slope we use. In particular, the slope scales the distance by a factor of sqrt(1+m^2) where m is the slope we use. + // This means that the slope we should use is the one such that mx+c=sqrt(1+m^2)(nx+b). This has a solution m=n/sqrt(1-n^2) when |n|<1. We set flatSlope = n and segmentSlope = m. + // Rewriting the above calculation of the y intercept, we get: + // mean([Math.Pow(1+segmentSlope*segmentSlope, 0.5)*pixDist[columnStartCoordinate+j, i]*objectRadius - segmentSlope*j*OSUPX_BETWEEN_ROWS for j in range(0, columnStartOffset+1)]) + // which is simplified to: + // Math.Pow(1+segmentSlope*segmentSlope, 0.5)*objectRadius*(pixDist[columnStartCoordinate, i] + gradientDist*(columnStartOffset+1)/2)-round(gradientDist*(columnStartOffset+1/2))/(columnStartOffset+1/2)*(columnStartOffset+1)/2 + double flatSlope = Math.Round(gradientDist * (columnStartOffset + 0.5)) / ((columnStartOffset + 0.5) * OSUPX_BETWEEN_ROWS); + if (flatSlope == 0) { + segmentSlope = 0; + } + else { + segmentSlope = flatSlope / Math.Pow(1 - flatSlope * flatSlope, 0.5); // This works because flatSlope <= 1/OSUPX_BETWEEN_ROWS << 1 + } + + relativeStartX = -leftToRight * OSUPX_BETWEEN_ROWS / 4; // This only works because OSUPX_BETWEEN_ROWS is a multiple of 4 + relativeStartY = (int)(segmentSlope * relativeStartX + Math.Pow(1 + segmentSlope * segmentSlope, 0.5) * objectRadius * (pixDist[columnStartCoordinate, i] + gradientDist * (columnStartOffset + 1) / 2) - segmentSlope * OSUPX_BETWEEN_ROWS * (columnStartOffset + 1) / 2); + absoluteStartX = (int)(relativeStartX + OSUPX_BETWEEN_ROWS * (columnStartCoordinate + 0.5) + imageStartOsuPx.X); + absoluteStartY = (int)(relativeStartY + OSUPX_BETWEEN_ROWS * (i + 0.5) + imageStartOsuPx.Y); + sliderPath.Add(new Vector2(absoluteStartX, absoluteStartY)); + sliderPath.Add(new Vector2(absoluteStartX + (columnStartOffset + leftToRight * 0.5) * OSUPX_BETWEEN_ROWS, Math.Round(absoluteStartY + gradientDist * columnStartOffset))); + + columnStartCoordinate = columnEndCoordinate + leftToRight; + + } + + sliderPath.Add(new Vector2(absoluteStartX + (columnStartOffset + 0.5) * OSUPX_BETWEEN_ROWS, absoluteStartY + gradientDist * columnStartOffset + OSUPX_BETWEEN_ROWS)); + } + + + + return sliderPath; + } + } + + +} \ No newline at end of file diff --git a/Mapping_Tools/MainWindow.xaml.cs b/Mapping_Tools/MainWindow.xaml.cs index d7ab3674..bf4f004d 100644 --- a/Mapping_Tools/MainWindow.xaml.cs +++ b/Mapping_Tools/MainWindow.xaml.cs @@ -37,6 +37,16 @@ public partial class MainWindow { public static HttpClient HttpClient { get; set; } public static SnackbarMessageQueue MessageQueue { get; set; } + public delegate void CurrentBeatmapUpdateHandler(object sender, string currentBeatmaps); + + public event CurrentBeatmapUpdateHandler OnUpdateCurrentBeatmap; + public void UpdateCurrentBeatmap(string currentBeatmaps) + { + // Make sure someone is listening to event + if (OnUpdateCurrentBeatmap == null) return; + OnUpdateCurrentBeatmap(this, currentBeatmaps); + } + public MainWindow() { try { AppWindow = this; @@ -201,12 +211,15 @@ public object GetCurrentView() { } public void SetCurrentMaps(string[] paths) { - ViewModel.CurrentBeatmaps = string.Join("|", paths); + string strPaths = string.Join("|", paths); + ViewModel.CurrentBeatmaps = strPaths; + UpdateCurrentBeatmap(strPaths); SettingsManager.AddRecentMap(paths, DateTime.Now); } public void SetCurrentMapsString(string paths) { ViewModel.CurrentBeatmaps = paths; + UpdateCurrentBeatmap(paths); SettingsManager.AddRecentMap(paths.Split('|'), DateTime.Now); } diff --git a/Mapping_Tools/Mapping_Tools.csproj b/Mapping_Tools/Mapping_Tools.csproj index af98a69e..590d9d06 100644 --- a/Mapping_Tools/Mapping_Tools.csproj +++ b/Mapping_Tools/Mapping_Tools.csproj @@ -62,6 +62,7 @@ + diff --git a/Mapping_Tools/Viewmodels/CommandHandler.cs b/Mapping_Tools/Viewmodels/CommandHandler.cs new file mode 100644 index 00000000..77001d64 --- /dev/null +++ b/Mapping_Tools/Viewmodels/CommandHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.Windows.Input; + +namespace Mapping_Tools.Viewmodels +{ + public class CommandHandler : ICommand + { + private Action _action; + private Func _canExecute; + + /// + /// Creates instance of the command handler + /// + /// Action to be executed by the command + /// A bolean property to containing current permissions to execute the command + public CommandHandler(Action action, Func canExecute) + { + _action = action; + _canExecute = canExecute; + } + + /// + /// Wires CanExecuteChanged event + /// + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + /// + /// Forcess checking if execute is allowed + /// + /// + /// + public bool CanExecute(object parameter) + { + return _canExecute.Invoke(); + } + + public void Execute(object parameter) + { + _action(); + } + } +} diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs new file mode 100644 index 00000000..7bc20d82 --- /dev/null +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -0,0 +1,408 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media.Imaging; +using Mapping_Tools.Classes; +using Mapping_Tools.Classes.BeatmapHelper; +using Mapping_Tools.Classes.SystemTools; +using Mapping_Tools.Classes.Tools.SlideratorStuff; +using Newtonsoft.Json; + +namespace Mapping_Tools.Viewmodels +{ + + public class SliderPicturatorVM : BindableBase + { + #region Properties + + private CancellationTokenSource previewTokenSource; + private readonly object previewTokenLock = new(); + + private bool _isProcessingPreview; + [JsonIgnore] + public bool IsProcessingPreview + { + get => _isProcessingPreview; + set => Set(ref _isProcessingPreview, value); + } + + [JsonIgnore] + public string[] Paths { get; set; } + + [JsonIgnore] + public bool Quick { get; set; } + + private long _viewportSize; + public long ViewportSize + { + get => _viewportSize; + set => Set(ref _viewportSize, value); + } + + [JsonIgnore] + public IEnumerable ViewportSizes => new List { 16384, 32768 }; + + private double _yResolution; + public double YResolution + { + get => _yResolution; + set => Set(ref _yResolution, value); + } + + private double _sliderStartX; + public double SliderStartX + { + get => _sliderStartX; + set => Set(ref _sliderStartX, value); + } + + private double _sliderStartY; + public double SliderStartY + { + get => _sliderStartY; + set => Set(ref _sliderStartY, value); + } + + private double _imageStartX; + public double ImageStartX + { + get => _imageStartX; + set => Set(ref _imageStartX, value); + } + + private double _imageStartY; + public double ImageStartY + { + get => _imageStartY; + set => Set(ref _imageStartY, value); + } + + [JsonIgnore] + public IEnumerable AvailableColors { + get + { + var beatmap = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()).Beatmap; + var comboColors = beatmap.ComboColours; + if (comboColors.Count() == 0) { + comboColors = ComboColour.GetDefaultComboColours().ToList(); + } + var availableColors = comboColors.Select(comboColor => Color.FromArgb(comboColor.Color.R, comboColor.Color.G, comboColor.Color.B)); + if (beatmap.SpecialColours.ContainsKey("SliderTrackOverride")) { + var tempColor = beatmap.SpecialColours["SliderTrackOverride"].Color; + availableColors.Append(Color.FromArgb(tempColor.R, tempColor.G, tempColor.B)); + } + return availableColors; + } + } + + + private bool _useMapComboColors; + + public bool UseMapComboColors + { + get => _useMapComboColors; + set + { + if (Set(ref _useMapComboColors, value)) { + if (value) { + CurrentTrackColor = ComboColor; + } else { + CurrentTrackColor = Color.FromArgb(TrackColorPickerColor.R, TrackColorPickerColor.G, TrackColorPickerColor.B); ; + } + RaisePropertyChanged(nameof(ShouldShowCCPicker)); + RaisePropertyChanged(nameof(ShouldShowPalette)); + } + } + } + + private Color _currentTrackColor; + public Color CurrentTrackColor + { + get => _currentTrackColor; + set + { + if (Set(ref _currentTrackColor, value)) { + RegeneratePreview(); + } + } + } + + private Color _comboColor; + public Color ComboColor + { + get => _comboColor; + set + { + if (Set(ref _comboColor, value) && UseMapComboColors) { + CurrentTrackColor = value; + RaisePropertyChanged(nameof(PickedComboColor)); + } + } + } + + public String PickedComboColor + { + get => ColorTranslator.ToHtml(ComboColor); + } + + private System.Windows.Media.Color _trackColorPickerColor; + + public System.Windows.Media.Color TrackColorPickerColor + { + get => _trackColorPickerColor; + set { + if(Set(ref _trackColorPickerColor, value) && !UseMapComboColors) { + CurrentTrackColor = Color.FromArgb(value.R, value.G, value.B); + RegeneratePreview(); + } + } + } + + private System.Windows.Media.Color _borderColor; + + public System.Windows.Media.Color BorderColor + { + get => _borderColor; + set + { + if (Set(ref _borderColor, value)) { + RegeneratePreview(); + } + } + } + + public Visibility ShouldShowCCPicker + { + get => UseMapComboColors ? Visibility.Visible : Visibility.Collapsed; + } + + public Visibility ShouldShowPalette + { + get => UseMapComboColors ? Visibility.Collapsed : Visibility.Visible; + } + + private int _timeCode; + public int TimeCode + { + get => _timeCode; + set => Set(ref _timeCode, value); + } + + private double _duration; + public double Duration + { + get => _duration; + set => Set(ref _duration, value); + } + + private ICommand _uploadFile; + public ICommand UploadFile + { + get + { + return _uploadFile ?? (_uploadFile = new CommandHandler(() => SetFile(), () => true)); + } + } + + + [System.Runtime.InteropServices.DllImport("gdi32.dll")] + public static extern bool DeleteObject(IntPtr hObject); + + private Bitmap _bm; + public Bitmap BM + { + get => _bm; + set => Set(ref _bm, value); + } + + private InteropBitmap _bmImage; + + public InteropBitmap BMImage + { + get => _bmImage; + set => Set(ref _bmImage, value); + } + + private string _pictureFile; + public string PictureFile + { + get => _pictureFile; + set { + Set(ref _pictureFile, value); + BM = new Bitmap(value); + RaisePropertyChanged(nameof(BM)); + RegeneratePreview(); + } + } + + private void SetFile() + { + OpenFileDialog fileDialog = new OpenFileDialog(); + fileDialog.DefaultExt = ".png"; // Required file extension + fileDialog.Filter = @"All Image Files|*.BMP;*.bmp;*.JPG;*.JPEG*.jpg;*.jpeg;*.PNG;*.png;*.GIF;*.gif;*.tif;*.tiff;*.ico;*.ICO|PNG|*.PNG;*.png|JPEG|*.JPG;*.JPEG*.jpg;*.jpeg|Bitmap(.BMP,.bmp)|*.BMP;*.bmp|GIF|*.GIF;*.gif|TIF|*.tif;*.tiff|ICO|*.ico;*.ICO";// Optional file extensions + + if (fileDialog.ShowDialog() == DialogResult.OK) { + PictureFile = fileDialog.FileName; + } + } + + private bool _blackOn; + public bool BlackOn + { + get => _blackOn; + set { + if (Set(ref _blackOn, value)) { + RegeneratePreview(); + } + } + } + + private bool _borderOn; + public bool BorderOn + { + get => _borderOn; + set { + if (Set(ref _borderOn, value)) { + RegeneratePreview(); + } + } + } + + private bool _redOn; + public bool RedOn + { + get => _redOn; + set { + if (Set(ref _redOn, value)) { + RegeneratePreview(); + } + } + } + + private bool _greenOn; + public bool GreenOn + { + get => _greenOn; + set { + if (Set(ref _greenOn, value)) { + RegeneratePreview(); + } + } + } + + private bool _blueOn; + public bool BlueOn + { + get => _blueOn; + set { + if (Set(ref _blueOn, value)) { + RegeneratePreview(); + } + } + } + + private bool _alphaOn; + public bool AlphaOn + { + get => _alphaOn; + set { + if (Set(ref _alphaOn, value)) { + RegeneratePreview(); + } + } + } + + #endregion + + public void RegeneratePreview() { + if (BM == null) { + return; + } + CancellationToken ct; + lock (previewTokenLock) { + if (previewTokenSource is not null) { + previewTokenSource.Cancel(); + previewTokenSource.Dispose(); + } + previewTokenSource = new CancellationTokenSource(); + ct = previewTokenSource.Token; + } + + // Raise property changed for the load indicator in the preview + IsProcessingPreview = true; + var beatmap = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()).Beatmap; + Bitmap bm = (Bitmap)BM.Clone(); + Color ctc = Color.FromArgb(CurrentTrackColor.ToArgb()); + Color bc = Color.FromArgb(BorderColor.R, BorderColor.G, BorderColor.B); + Task.Run(() => { + Bitmap newBM = SliderPicturator.Recolor(bm, ctc, bc, Color.FromArgb(0, 0, 0), !BlackOn, !BorderOn, !AlphaOn, RedOn, GreenOn, BlueOn); + // Send the new preview to the main thread + System.Windows.Application.Current.Dispatcher.Invoke(() => { + IntPtr hBitmap = newBM.GetHbitmap(); + InteropBitmap retval; + + try { + retval = (InteropBitmap)Imaging.CreateBitmapSourceFromHBitmap( + hBitmap, + IntPtr.Zero, + Int32Rect.Empty, + BitmapSizeOptions.FromEmptyOptions()); + } + finally { + DeleteObject(hBitmap); + } + BMImage = retval; + RaisePropertyChanged(nameof(BMImage)); + }); + + }).ContinueWith(task => { + // Show the error if one occured while generating preview + if (task.IsFaulted) { + task.Exception.Show(); + } + // Stop the processing indicator + System.Windows.Application.Current.Dispatcher.Invoke(() => { + IsProcessingPreview = false; + }); + }, ct); + } + + private void HandleCurrentBeatmapUpdate(object sender, string currentBeatmaps) + { + RaisePropertyChanged(nameof(AvailableColors)); + } + + public SliderPicturatorVM() + { + MainWindow.AppWindow.OnUpdateCurrentBeatmap += HandleCurrentBeatmapUpdate; + ViewportSize = 16384; + TimeCode = 0; + Duration = 1; + YResolution = 1080; + SliderStartX = 256; + SliderStartY = 192; + ImageStartX = 0; + ImageStartY = 0; + BlackOn = true; + BorderOn = true; + RedOn = true; + GreenOn = true; + BlueOn = true; + AlphaOn = true; + UseMapComboColors = false; + ComboColor = Color.FromArgb(0, 0, 0); + TrackColorPickerColor = System.Windows.Media.Color.FromArgb(255, 255, 255, 255); + BorderColor = System.Windows.Media.Color.FromArgb(255, 255, 255, 255); + BMImage = null; + BM = null; + } + } + +} \ No newline at end of file diff --git a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml new file mode 100644 index 00000000..f3680978 --- /dev/null +++ b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + Change the length and duration of marked sliders and this tool will automatically handle the SliderVelocity for you. + + + + + + + + This tool is compatible with QuickRun! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs new file mode 100644 index 00000000..a4a7d8d2 --- /dev/null +++ b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Input; +using System.Drawing; +using System.Drawing.Imaging; +using Mapping_Tools.Classes; +using Mapping_Tools.Classes.BeatmapHelper; +using Mapping_Tools.Classes.BeatmapHelper.Enums; +using Mapping_Tools.Classes.MathUtil; +using Mapping_Tools.Classes.SystemTools; +using Mapping_Tools.Classes.SystemTools.QuickRun; +using Mapping_Tools.Classes.ToolHelpers; +using Mapping_Tools.Classes.Tools.SlideratorStuff; +using Mapping_Tools.Classes.Tools; +using Mapping_Tools.Viewmodels; +using System.Runtime.InteropServices; + +namespace Mapping_Tools.Views.SliderPicturator { + /// + /// Interaktionslogik für UserControl1.xaml + /// + [SmartQuickRunUsage(SmartQuickRunTargets.AnySelection)] + [VerticalContentScroll] + [HorizontalContentScroll] + public partial class SliderPicturatorView : IQuickRun, ISavable { + public event EventHandler RunFinished; + + public static readonly string ToolName = "Slider Picturator"; + + public static readonly string ToolDescription = $@"Import an image and this program will distort a slider into it!"; + + /// + public SliderPicturatorView() + { + InitializeComponent(); + Width = MainWindow.AppWindow.content_views.Width; + Height = MainWindow.AppWindow.content_views.Height; + DataContext = new SliderPicturatorVM(); + ProjectManager.LoadProject(this, message: false); + } + + public SliderPicturatorVM ViewModel => (SliderPicturatorVM) DataContext; + + protected override void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { + var bgw = sender as BackgroundWorker; + e.Result = Picturate((SliderPicturatorVM) e.Argument, bgw, e); + } + + + private void Start_Click(object sender, RoutedEventArgs e) { + RunTool(new[] {IOHelper.GetCurrentBeatmapOrCurrentBeatmap()}); + } + + public void QuickRun() { + RunTool(new[] { IOHelper.GetCurrentBeatmapOrCurrentBeatmap() }, quick: true); + } + + private void RunTool(string[] paths, bool quick = false) { + if (!CanRun) return; + + // Remove logical focus to trigger LostFocus on any fields that didn't yet update the ViewModel + FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), null); + + BackupManager.SaveMapBackup(paths); + + ViewModel.Paths = paths; + ViewModel.Quick = quick; + + BackgroundWorker.RunWorkerAsync(ViewModel); + CanRun = false; + } + + private string Picturate(SliderPicturatorVM arg, BackgroundWorker worker, DoWorkEventArgs _) { + int slidersCompleted = 0; + + var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); + + if (arg.PictureFile == null) { + throw new Exception("No image file selected.", editorReaderException1); + } + + Bitmap img; + try { + img = new Bitmap(arg.PictureFile); + } catch { + throw new Exception("Not a valid image file."); + } + + BeatmapEditor editor = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()); + + var beatmap = editor.Beatmap; + long GPU = arg.ViewportSize; + bool R = arg.RedOn; + bool G = arg.GreenOn; + bool B = arg.BlueOn; + bool borderOff = !arg.BorderOn; + bool blackOff = !arg.BlackOn; + double startTime = arg.TimeCode; + double startPosX = arg.SliderStartX; + double startPosY = arg.SliderStartY; + double startPosPicX = arg.ImageStartX; + double startPosPicY = arg.ImageStartY; + double duration = arg.Duration; + double resY = arg.YResolution; + Vector2 startPos = new Vector2(startPosX, startPosY); + Vector2 startPosPic = new Vector2(startPosPicX, startPosPicY); + + var circleSize = beatmap.Difficulty["CircleSize"].DoubleValue; + Color sliderColor = arg.CurrentTrackColor; + Color borderColor = Color.FromArgb(arg.BorderColor.R, arg.BorderColor.G, arg.BorderColor.B); + Color backgroundColor = Color.FromArgb(0,0,0); + + //string[] files = Directory.GetFiles(@"C:\Users\User\Desktop\badapple\images-6fps@360p", "*.png"); + //double TIME_SPACING = 1000/6; + //double time = 0; + //HitObject ho; + //List sliderPath; + //foreach (string file in files) { + // img = new Bitmap(file); + // sliderPath = SliderPicturator.Picturate(img, sliderColor, sliderBorder, backgroundColor, circleSize, resY, GPU, false, true); + // ho = new HitObject(time, 0, SampleSet.None, SampleSet.None) + // { + // IsCircle = false, + // IsSpinner = false, + // IsHoldNote = false, + // IsSlider = true + // }; + // ho.SetAllCurvePoints(sliderPath); + // ho.SliderType = PathType.Linear; + // ho.PixelLength = OsuStableDistance(sliderPath); + // beatmap.HitObjects.Add(ho); + // time += TIME_SPACING; + // worker.ReportProgress((int)Math.Round(100 * (time / TIME_SPACING) / files.Length)); + //} + + List sliderPath = Classes.Tools.SlideratorStuff.SliderPicturator.Picturate(img, sliderColor, borderColor, backgroundColor, circleSize, startPos, startPosPic, resY, GPU, blackOff, borderOff, R, G, B); + + // Find nearest hitobject before startTime and get its combo color index + int currentColorIdx = 0; + int idx = beatmap.HitObjects.Select((hitObject) => hitObject.Time).ToList().BinarySearch(startTime); + if (idx < 0) { + idx = ~idx - 1; + } + if (idx >= 0) { + currentColorIdx = beatmap.HitObjects[idx].ColourIndex; + } + + // Get requested color's combo color index, if it exists + int foundColorIdx = beatmap.ComboColours.FindIndex(cc => cc.Color.R == sliderColor.R && cc.Color.G == sliderColor.G && cc.Color.B == sliderColor.B); + if (foundColorIdx == -1) { + foundColorIdx = 0; + } + + var ho = new HitObject(startTime, 0, SampleSet.None, SampleSet.None) + { + IsCircle = false, + IsSpinner = false, + IsHoldNote = false, + IsSlider = true, + ComboSkip = foundColorIdx - currentColorIdx - 1 + }; + ho.SetAllCurvePoints(sliderPath); + ho.SliderType = PathType.Linear; + ho.PixelLength = OsuStableDistance(sliderPath); + beatmap.HitObjects.Add(ho); + beatmap.SortHitObjects(); + + var timing = beatmap.BeatmapTiming; + var tpAfter = timing.GetRedlineAtTime(ho.Time).Copy(); + var tpOn = tpAfter.Copy(); + + tpAfter.Offset = ho.Time; + tpOn.Offset = ho.Time - 1; // This one will be on the slider + + tpAfter.OmitFirstBarLine = true; + tpOn.OmitFirstBarLine = true; + + // Express velocity in BPM + // We want ho.PixelLength = 5/3*timing.SliderMultiplier*bpm*duration/1000 so bpm = ho.PixelLength*600/(timing.SliderMultiplier*duration). Converting to MpB we get 60000*(timing.SliderMultiplier*duration/(ho.PixelLength*600)) = 100*timing.SliderMultiplier*duration/ho.PixelLength + tpOn.MpB = 100 * timing.SliderMultiplier * duration / ho.PixelLength; + // NaN SV results in removal of slider ticks + ho.SliderVelocity = double.NaN; + + // Add redlines + var timingPointsChanges = new List(); + timingPointsChanges.Add(new TimingPointsChange(tpOn, mpb: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)); + timingPointsChanges.Add(new TimingPointsChange(tpAfter, mpb: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)); + + ho.Time -= 1; + + timingPointsChanges.AddRange(beatmap.HitObjects.Select(bmho => { + var sv = bmho == ho ? bmho.SliderVelocity : timing.GetSvAtTime(bmho.Time); + var tp = timing.GetTimingPointAtTime(bmho.Time).Copy(); + tp.MpB = sv; + tp.Offset = bmho.Time; + return new TimingPointsChange(tp, mpb: true, fuzzyness: Precision.DOUBLE_EPSILON); + })); + + TimingPointsChange.ApplyChanges(timing, timingPointsChanges); + + editor.SaveFile(); + + // Complete progressbar + if (worker != null && worker.WorkerReportsProgress) + { + worker.ReportProgress(100); + } + + // Do stuff + RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, arg.Quick)); + return arg.Quick ? "" : "Done!"; + } + public SliderPicturatorVM GetSaveData() { + return ViewModel; + } + + public void SetSaveData(SliderPicturatorVM saveData) { + DataContext = saveData; + } + + public string AutoSavePath => Path.Combine(MainWindow.AppDataPath, "sliderPicturatorproject.json"); + + public string DefaultSaveFolder => Path.Combine(MainWindow.AppDataPath, "Slider Picturator Projects"); + + private Color getOpaqueColor(Color top, Color bottom) + { + // Bottom color is assumed to be opaque + double GAMMA = 1; + return Color.FromArgb(255, + (byte)Math.Round(Math.Pow(Math.Pow(bottom.R, GAMMA) * (1 - top.A) + Math.Pow(top.R, GAMMA) * top.A, 1 / GAMMA)), + (byte)Math.Round(Math.Pow(Math.Pow(bottom.G, GAMMA) * (1 - top.A) + Math.Pow(top.G, GAMMA) * top.A, 1 / GAMMA)), + (byte)Math.Round(Math.Pow(Math.Pow(bottom.B, GAMMA) * (1 - top.A) + Math.Pow(top.B, GAMMA) * top.A, 1 / GAMMA))); + } + private static void MySaveBMP(byte[] buffer, int width, int height, String loc) + { + Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb); + + Rectangle BoundsRect = new Rectangle(0, 0, width, height); + BitmapData bmpData = b.LockBits(BoundsRect, + ImageLockMode.WriteOnly, + b.PixelFormat); + + IntPtr ptr = bmpData.Scan0; + + // add back dummy bytes between lines, make each line be a multiple of 4 bytes + int skipByte = bmpData.Stride - width * 3; + byte[] newBuff = new byte[buffer.Length + skipByte * height]; + for (int j = 0; j < height; j++) { + Buffer.BlockCopy(buffer, j * width * 3, newBuff, j * (width * 3 + skipByte), width * 3); + } + + // fill in rgbValues + Marshal.Copy(newBuff, 0, ptr, newBuff.Length); + b.UnlockBits(bmpData); + b.Save(loc, ImageFormat.Bmp); + } + + private static double OsuStableDistance(IEnumerable controlPoints) + { + double length = 0; + Vector2 cp, lp; + float num1, num2, num3, cpX, cpY, lpX, lpY; + for (int i = 1; i < controlPoints.Count(); i++) { + lp = controlPoints.ElementAt(i - 1); + cp = controlPoints.ElementAt(i); + num1 = (float)Math.Round(lp.X) - (float)Math.Round(cp.X); + num2 = (float)Math.Round(lp.Y) - (float)Math.Round(cp.Y); + num3 = num1 * num1 + num2 * num2; + + length += (float)Math.Sqrt((double)num3); + } + return length; + } + } +} From 9f56a35c57477587da9b1ad105337657389859ed Mon Sep 17 00:00:00 2001 From: Jonathan Keegan Date: Sat, 12 Nov 2022 23:01:47 -0500 Subject: [PATCH 02/26] fix alpha and lighten amount to match osu! calculations --- .../Classes/Tools/SlideratorStuff/SliderPicturator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs index f46d1328..def679e1 100644 --- a/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs +++ b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderPicturator.cs @@ -10,9 +10,9 @@ namespace Mapping_Tools.Classes.Tools.SlideratorStuff public static class SliderPicturator { - private const double LIGHTEN_AMOUNT = 0.5; + private const double LIGHTEN_AMOUNT = 0.25; private const double DARKEN_AMOUNT = 0.1; - private const byte ALPHA = 100; + private const byte ALPHA = 180; public static int SNAPTOL => (int)Math.Pow(2, 5) * 3; private static Color getOpaqueColor(Color top, Color bottom) { From ff5a96065e436fe5fde4f4a24f90395576c638a6 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 21:00:49 +0100 Subject: [PATCH 03/26] fix crash when current beatmap doesnt exist --- .../Viewmodels/SliderPicturatorVM.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index 7bc20d82..0415e149 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -17,7 +17,6 @@ namespace Mapping_Tools.Viewmodels { - public class SliderPicturatorVM : BindableBase { #region Properties @@ -88,17 +87,27 @@ public double ImageStartY public IEnumerable AvailableColors { get { - var beatmap = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()).Beatmap; - var comboColors = beatmap.ComboColours; - if (comboColors.Count() == 0) { - comboColors = ComboColour.GetDefaultComboColours().ToList(); + try { + var path = MainWindow.AppWindow.GetCurrentMaps()[0]; + var beatmap = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()).Beatmap; + var comboColors = beatmap.ComboColours; + + if (comboColors.Count == 0) { + comboColors = ComboColour.GetDefaultComboColours().ToList(); + } + + var availableColors = comboColors.Select(comboColor => Color.FromArgb(comboColor.Color.R, comboColor.Color.G, comboColor.Color.B)); + if (beatmap.SpecialColours.ContainsKey("SliderTrackOverride")) { + var tempColor = beatmap.SpecialColours["SliderTrackOverride"].Color; + availableColors = availableColors.Append(Color.FromArgb(tempColor.R, tempColor.G, tempColor.B)); + } + + return availableColors; } - var availableColors = comboColors.Select(comboColor => Color.FromArgb(comboColor.Color.R, comboColor.Color.G, comboColor.Color.B)); - if (beatmap.SpecialColours.ContainsKey("SliderTrackOverride")) { - var tempColor = beatmap.SpecialColours["SliderTrackOverride"].Color; - availableColors.Append(Color.FromArgb(tempColor.R, tempColor.G, tempColor.B)); + catch (Exception e) { + Console.WriteLine(e); + return Enumerable.Empty(); } - return availableColors; } } @@ -337,7 +346,6 @@ public void RegeneratePreview() { // Raise property changed for the load indicator in the preview IsProcessingPreview = true; - var beatmap = new BeatmapEditor(IOHelper.GetCurrentBeatmapOrCurrentBeatmap()).Beatmap; Bitmap bm = (Bitmap)BM.Clone(); Color ctc = Color.FromArgb(CurrentTrackColor.ToArgb()); Color bc = Color.FromArgb(BorderColor.R, BorderColor.G, BorderColor.B); @@ -362,7 +370,7 @@ public void RegeneratePreview() { RaisePropertyChanged(nameof(BMImage)); }); - }).ContinueWith(task => { + }, ct).ContinueWith(task => { // Show the error if one occured while generating preview if (task.IsFaulted) { task.Exception.Show(); From 2102526ac517f1006f1abbcd7b0d3538c142e39b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:02:46 +0100 Subject: [PATCH 04/26] cleaned up code --- .../Tools/SlideratorStuff/SliderInvisiblator.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderInvisiblator.cs b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderInvisiblator.cs index b7812474..59988188 100644 --- a/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderInvisiblator.cs +++ b/Mapping_Tools/Classes/Tools/SlideratorStuff/SliderInvisiblator.cs @@ -57,7 +57,7 @@ public static (Vector2[], double) Invisiblate(int duration, Vector2[] sbPosition double frameDist = OsuStableDistance(curMsPath) - 2 * SNAPTOL / 3; double MpB = 100 * globalSV / frameDist; - MpB = Double.Parse(MpB.ToString()); + MpB = double.Parse(MpB.ToString()); frameDist = 100 * globalSV / MpB; @@ -91,9 +91,6 @@ public static (Vector2[], double) Invisiblate(int duration, Vector2[] sbPosition // Update ctrlPtIdx ctrlPtIdx += curMsPath.Count - 1; - - double wtf = OsuStableDistance(controlPoints.Take(ctrlPtIdx)) % frameDist; - } Vector2[] newControlPoints = new Vector2[ctrlPtIdx+2]; Array.Copy(controlPoints, newControlPoints, ctrlPtIdx); @@ -106,19 +103,19 @@ public static (Vector2[], double) Invisiblate(int duration, Vector2[] sbPosition return (newControlPoints, frameDist); } - private static double OsuStableDistance(IEnumerable controlPoints) + private static double OsuStableDistance(List controlPoints) { double length = 0; Vector2 cp, lp; - float num1, num2, num3, cpX, cpY, lpX, lpY; - for (int i = 1; i < controlPoints.Count(); i++) { + float num1, num2, num3; + for (int i = 1; i < controlPoints.Count; i++) { lp = controlPoints.ElementAt(i - 1); cp = controlPoints.ElementAt(i); num1 = (float)Math.Round(lp.X)- (float)Math.Round(cp.X); num2 = (float)Math.Round(lp.Y) - (float)Math.Round(cp.Y); num3 = num1 * num1 + num2 * num2; - length += (float)Math.Sqrt((double)num3); + length += (float)Math.Sqrt(num3); } return length; } From 8ec2413d3afb679488a9463f8dfde57accb1bc31 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:03:42 +0100 Subject: [PATCH 05/26] removed CommandHandler bloat --- Mapping_Tools/Viewmodels/CommandHandler.cs | 46 ------------------- .../Viewmodels/SliderPicturatorVM.cs | 13 ++---- 2 files changed, 4 insertions(+), 55 deletions(-) delete mode 100644 Mapping_Tools/Viewmodels/CommandHandler.cs diff --git a/Mapping_Tools/Viewmodels/CommandHandler.cs b/Mapping_Tools/Viewmodels/CommandHandler.cs deleted file mode 100644 index 77001d64..00000000 --- a/Mapping_Tools/Viewmodels/CommandHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Windows.Input; - -namespace Mapping_Tools.Viewmodels -{ - public class CommandHandler : ICommand - { - private Action _action; - private Func _canExecute; - - /// - /// Creates instance of the command handler - /// - /// Action to be executed by the command - /// A bolean property to containing current permissions to execute the command - public CommandHandler(Action action, Func canExecute) - { - _action = action; - _canExecute = canExecute; - } - - /// - /// Wires CanExecuteChanged event - /// - public event EventHandler CanExecuteChanged - { - add { CommandManager.RequerySuggested += value; } - remove { CommandManager.RequerySuggested -= value; } - } - - /// - /// Forcess checking if execute is allowed - /// - /// - /// - public bool CanExecute(object parameter) - { - return _canExecute.Invoke(); - } - - public void Execute(object parameter) - { - _action(); - } - } -} diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index 0415e149..d84ad045 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -211,15 +211,8 @@ public double Duration set => Set(ref _duration, value); } - private ICommand _uploadFile; - public ICommand UploadFile - { - get - { - return _uploadFile ?? (_uploadFile = new CommandHandler(() => SetFile(), () => true)); - } - } - + [JsonIgnore] + public CommandImplementation UploadFileCommand { get; } [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); @@ -410,6 +403,8 @@ public SliderPicturatorVM() BorderColor = System.Windows.Media.Color.FromArgb(255, 255, 255, 255); BMImage = null; BM = null; + + UploadFileCommand = new CommandImplementation(_ => SetFile()); } } From dd13e774ec0af80dfe591871dd6f28d62b3ce2aa Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:05:55 +0100 Subject: [PATCH 06/26] renamed VM to Vm --- Mapping_Tools/Viewmodels/SliderPicturatorVM.cs | 4 ++-- .../SliderPicturator/SliderPicturatorView.xaml | 2 +- .../SliderPicturator/SliderPicturatorView.xaml.cs | 14 ++++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index d84ad045..3fe68a86 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -17,7 +17,7 @@ namespace Mapping_Tools.Viewmodels { - public class SliderPicturatorVM : BindableBase + public class SliderPicturatorVm : BindableBase { #region Properties @@ -380,7 +380,7 @@ private void HandleCurrentBeatmapUpdate(object sender, string currentBeatmaps) RaisePropertyChanged(nameof(AvailableColors)); } - public SliderPicturatorVM() + public SliderPicturatorVm() { MainWindow.AppWindow.OnUpdateCurrentBeatmap += HandleCurrentBeatmapUpdate; ViewportSize = 16384; diff --git a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml index f3680978..fc211799 100644 --- a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml +++ b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml @@ -9,7 +9,7 @@ mc:Ignorable="d" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" x:Name="This" - d:DesignHeight="450" Width="759.5" d:DataContext="{d:DesignData SliderPicturerVM}"> + d:DesignHeight="450" Width="759.5" d:DataContext="{d:DesignData SliderPicturatorVm}"> diff --git a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs index a4a7d8d2..21629de6 100644 --- a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs +++ b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml.cs @@ -26,7 +26,7 @@ namespace Mapping_Tools.Views.SliderPicturator { [SmartQuickRunUsage(SmartQuickRunTargets.AnySelection)] [VerticalContentScroll] [HorizontalContentScroll] - public partial class SliderPicturatorView : IQuickRun, ISavable { + public partial class SliderPicturatorView : IQuickRun, ISavable { public event EventHandler RunFinished; public static readonly string ToolName = "Slider Picturator"; @@ -39,11 +39,11 @@ public SliderPicturatorView() InitializeComponent(); Width = MainWindow.AppWindow.content_views.Width; Height = MainWindow.AppWindow.content_views.Height; - DataContext = new SliderPicturatorVM(); + DataContext = new SliderPicturatorVm(); ProjectManager.LoadProject(this, message: false); } - public SliderPicturatorVM ViewModel => (SliderPicturatorVM) DataContext; + public SliderPicturatorVm ViewModel => (SliderPicturatorVm) DataContext; protected override void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { var bgw = sender as BackgroundWorker; @@ -74,9 +74,7 @@ private void RunTool(string[] paths, bool quick = false) { CanRun = false; } - private string Picturate(SliderPicturatorVM arg, BackgroundWorker worker, DoWorkEventArgs _) { - int slidersCompleted = 0; - + private string Picturate(SliderPicturatorVm arg, BackgroundWorker worker, DoWorkEventArgs _) { var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); if (arg.PictureFile == null) { @@ -214,11 +212,11 @@ private string Picturate(SliderPicturatorVM arg, BackgroundWorker worker, DoWork RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, arg.Quick)); return arg.Quick ? "" : "Done!"; } - public SliderPicturatorVM GetSaveData() { + public SliderPicturatorVm GetSaveData() { return ViewModel; } - public void SetSaveData(SliderPicturatorVM saveData) { + public void SetSaveData(SliderPicturatorVm saveData) { DataContext = saveData; } From c28b6a956f0ddbcf7e59e212b1aa635aed9d840b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:06:07 +0100 Subject: [PATCH 07/26] moved SetFile --- .../Viewmodels/SliderPicturatorVM.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index 3fe68a86..61815010 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -244,17 +244,6 @@ public string PictureFile } } - private void SetFile() - { - OpenFileDialog fileDialog = new OpenFileDialog(); - fileDialog.DefaultExt = ".png"; // Required file extension - fileDialog.Filter = @"All Image Files|*.BMP;*.bmp;*.JPG;*.JPEG*.jpg;*.jpeg;*.PNG;*.png;*.GIF;*.gif;*.tif;*.tiff;*.ico;*.ICO|PNG|*.PNG;*.png|JPEG|*.JPG;*.JPEG*.jpg;*.jpeg|Bitmap(.BMP,.bmp)|*.BMP;*.bmp|GIF|*.GIF;*.gif|TIF|*.tif;*.tiff|ICO|*.ico;*.ICO";// Optional file extensions - - if (fileDialog.ShowDialog() == DialogResult.OK) { - PictureFile = fileDialog.FileName; - } - } - private bool _blackOn; public bool BlackOn { @@ -406,6 +395,17 @@ public SliderPicturatorVm() UploadFileCommand = new CommandImplementation(_ => SetFile()); } + + private void SetFile() + { + OpenFileDialog fileDialog = new OpenFileDialog(); + fileDialog.DefaultExt = ".png"; // Required file extension + fileDialog.Filter = @"All Image Files|*.BMP;*.bmp;*.JPG;*.JPEG*.jpg;*.jpeg;*.PNG;*.png;*.GIF;*.gif;*.tif;*.tiff;*.ico;*.ICO|PNG|*.PNG;*.png|JPEG|*.JPG;*.JPEG*.jpg;*.jpeg|Bitmap(.BMP,.bmp)|*.BMP;*.bmp|GIF|*.GIF;*.gif|TIF|*.tif;*.tiff|ICO|*.ico;*.ICO";// Optional file extensions + + if (fileDialog.ShowDialog() == DialogResult.OK) { + PictureFile = fileDialog.FileName; + } + } } } \ No newline at end of file From 6848fba600d4e07c81e8c9d1776b35a328435969 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:07:03 +0100 Subject: [PATCH 08/26] add more JsonIgnore to fix config --- .../Viewmodels/SliderPicturatorVM.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index 61815010..d44d39d0 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -156,10 +156,8 @@ public Color ComboColor } } - public String PickedComboColor - { - get => ColorTranslator.ToHtml(ComboColor); - } + [JsonIgnore] + public string PickedComboColor => ColorTranslator.ToHtml(ComboColor); private System.Windows.Media.Color _trackColorPickerColor; @@ -187,15 +185,11 @@ public System.Windows.Media.Color BorderColor } } - public Visibility ShouldShowCCPicker - { - get => UseMapComboColors ? Visibility.Visible : Visibility.Collapsed; - } + [JsonIgnore] + public Visibility ShouldShowCCPicker => UseMapComboColors ? Visibility.Visible : Visibility.Collapsed; - public Visibility ShouldShowPalette - { - get => UseMapComboColors ? Visibility.Collapsed : Visibility.Visible; - } + [JsonIgnore] + public Visibility ShouldShowPalette => UseMapComboColors ? Visibility.Collapsed : Visibility.Visible; private int _timeCode; public int TimeCode @@ -218,6 +212,7 @@ public double Duration public static extern bool DeleteObject(IntPtr hObject); private Bitmap _bm; + [JsonIgnore] public Bitmap BM { get => _bm; @@ -225,7 +220,7 @@ public Bitmap BM } private InteropBitmap _bmImage; - + [JsonIgnore] public InteropBitmap BMImage { get => _bmImage; From 152225e025e9169f133aa42af29dae3b015e94fb Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 13 Nov 2022 22:11:00 +0100 Subject: [PATCH 09/26] update description --- Mapping_Tools/Viewmodels/SliderPicturatorVM.cs | 2 +- .../Views/SliderPicturator/SliderPicturatorView.xaml | 4 ++-- .../Views/SliderPicturator/SliderPicturatorView.xaml.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs index d44d39d0..83afda36 100644 --- a/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs +++ b/Mapping_Tools/Viewmodels/SliderPicturatorVM.cs @@ -6,13 +6,13 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; -using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media.Imaging; using Mapping_Tools.Classes; using Mapping_Tools.Classes.BeatmapHelper; using Mapping_Tools.Classes.SystemTools; using Mapping_Tools.Classes.Tools.SlideratorStuff; +using Mapping_Tools.Components.Domain; using Newtonsoft.Json; namespace Mapping_Tools.Viewmodels diff --git a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml index fc211799..35c03a03 100644 --- a/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml +++ b/Mapping_Tools/Views/SliderPicturator/SliderPicturatorView.xaml @@ -31,7 +31,7 @@ - Change the length and duration of marked sliders and this tool will automatically handle the SliderVelocity for you. + Import an image and this program will distort a slider into it! To get started click the Browse button to select an image, then play with the colors and options until it looks right. Click the run button to export the slider picture at the specified time and position. @@ -47,7 +47,7 @@