From 318682b63aaef7906254390d8490a5131786a5a4 Mon Sep 17 00:00:00 2001 From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com> Date: Sun, 29 Jun 2025 12:20:45 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix:=E7=89=88=E6=9C=AC=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/AssemblyInfo.cs | 4 +-- Ink Canvas/Helpers/AutoUpdateHelper.cs | 45 ++++++++++++++++++++++---- Ink Canvas/Properties/AssemblyInfo.cs | 4 +-- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs index cfc6a3d3..c52e2153 100644 --- a/Ink Canvas/AssemblyInfo.cs +++ b/Ink Canvas/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.7.0.1")] +[assembly: AssemblyFileVersion("1.7.0.1")] diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs index 201cab11..3058aa97 100644 --- a/Ink Canvas/Helpers/AutoUpdateHelper.cs +++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs @@ -807,17 +807,48 @@ namespace Ink_Canvas.Helpers { LogHelper.WriteLogToFile($"AutoUpdate | Starting version fix for {channel} channel"); - // 获取当前通道的最新版本 - string latestVersion = await CheckForUpdates(null, channel); + // 获取远程版本号,而不是检查更新 + string remoteVersion = null; + string proxy = null; - if (string.IsNullOrEmpty(latestVersion)) + // 根据通道选择URL + string primaryUrl, fallbackUrl; + + if (channel == UpdateChannel.Release) { - LogHelper.WriteLogToFile("AutoUpdate | No newer version found for fixing", LogHelper.LogType.Warning); + // Release通道版本信息地址 + primaryUrl = "https://github.com/InkCanvasForClass/community/raw/refs/heads/beta/AutomaticUpdateVersionControl.txt"; + fallbackUrl = "https://bgithub.xyz/InkCanvasForClass/community/raw/refs/heads/main/AutomaticUpdateVersionControl.txt"; + } + else + { + // Beta通道版本信息地址 + primaryUrl = "https://github.com/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt"; + fallbackUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt"; + } + + LogHelper.WriteLogToFile($"AutoUpdate | Retrieving remote version from {channel} channel"); + + // 先尝试主地址 + remoteVersion = await GetRemoteVersion(primaryUrl); + + // 如果主地址失败,尝试备用地址 + if (remoteVersion == null) + { + LogHelper.WriteLogToFile($"AutoUpdate | Primary URL failed, trying fallback URL"); + remoteVersion = await GetRemoteVersion(fallbackUrl); + } + + if (string.IsNullOrEmpty(remoteVersion)) + { + LogHelper.WriteLogToFile("AutoUpdate | Failed to retrieve remote version for fixing", LogHelper.LogType.Error); return false; } - // 下载最新版本 - bool downloadResult = await DownloadSetupFileAndSaveStatus(latestVersion, "", channel); + LogHelper.WriteLogToFile($"AutoUpdate | Remote version for fixing: {remoteVersion}"); + + // 无论版本是否为最新,都下载远程版本 + bool downloadResult = await DownloadSetupFileAndSaveStatus(remoteVersion, "", channel); if (!downloadResult) { @@ -826,7 +857,7 @@ namespace Ink_Canvas.Helpers } // 执行安装,非静默模式 - InstallNewVersionApp(latestVersion, false); + InstallNewVersionApp(remoteVersion, false); // 设置为用户主动退出,避免被看门狗判定为崩溃 App.IsAppExitByUser = true; diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs index ed7a4d2a..2f1690a3 100644 --- a/Ink Canvas/Properties/AssemblyInfo.cs +++ b/Ink Canvas/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.7.0.1")] +[assembly: AssemblyFileVersion("1.7.0.1")] From ec330aea6930120288939c2663e5dee2d389abae Mon Sep 17 00:00:00 2001 From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com> Date: Sun, 29 Jun 2025 12:40:15 +0800 Subject: [PATCH 2/6] =?UTF-8?q?improve:=E6=94=B9=E8=BF=9B=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/LogHelper.cs | 70 ++++++++++++++++++- Ink Canvas/MainWindow.xaml | 11 ++- Ink Canvas/MainWindow_cs/MW_Settings.cs | 6 ++ Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 29 +++----- Ink Canvas/Resources/Settings.cs | 3 + 5 files changed, 96 insertions(+), 23 deletions(-) diff --git a/Ink Canvas/Helpers/LogHelper.cs b/Ink Canvas/Helpers/LogHelper.cs index a2c6ce66..b365636b 100644 --- a/Ink Canvas/Helpers/LogHelper.cs +++ b/Ink Canvas/Helpers/LogHelper.cs @@ -8,6 +8,9 @@ namespace Ink_Canvas.Helpers class LogHelper { public static string LogFile = "Log.txt"; + private static string LogsFolder = "Logs"; + private static string AppStartTime = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"); + private static readonly long MaxLogsFolderSizeBytes = 5 * 1024 * 1024; // 5MB public static void NewLog(string str) { @@ -28,14 +31,40 @@ namespace Ink_Canvas.Helpers public static void WriteLogToFile(string str, LogType logType = LogType.Info) { + // 检查日志是否启用 + if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && !MainWindow.Settings.Advanced.IsLogEnabled) return; + string strLogType = logType.ToString(); try { - var file = App.RootPath + LogFile; + string file; + + // 检查是否启用了日期保存功能 + if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && MainWindow.Settings.Advanced.IsSaveLogByDate) + { + // 确保Logs文件夹存在 + string logsPath = Path.Combine(App.RootPath, LogsFolder); + if (!Directory.Exists(logsPath)) + { + Directory.CreateDirectory(logsPath); + } + + // 检查Logs文件夹大小,如果超过5MB则清空 + CheckAndCleanLogsFolder(logsPath); + + // 使用软件启动时间作为日志文件名 + file = Path.Combine(logsPath, $"Log_{AppStartTime}.txt"); + } + else + { + file = App.RootPath + LogFile; + } + if (!Directory.Exists(App.RootPath)) { Directory.CreateDirectory(App.RootPath); } + var threadId = Thread.CurrentThread.ManagedThreadId; var callingMethod = new StackTrace(2, true).GetFrame(0); string callerInfo = ""; @@ -57,6 +86,45 @@ namespace Ink_Canvas.Helpers catch { } } + private static void CheckAndCleanLogsFolder(string logsPath) + { + try + { + long totalSize = 0; + DirectoryInfo dirInfo = new DirectoryInfo(logsPath); + + // 如果目录不存在,直接返回 + if (!dirInfo.Exists) return; + + // 计算文件夹大小 + foreach (FileInfo file in dirInfo.GetFiles()) + { + totalSize += file.Length; + } + + // 如果超过5MB,清空文件夹 + if (totalSize > MaxLogsFolderSizeBytes) + { + foreach (FileInfo file in dirInfo.GetFiles()) + { + try + { + file.Delete(); + } + catch { } + } + + // 记录清理操作 + string cleanupMessage = $"Logs folder exceeded size limit ({totalSize / 1024.0 / 1024.0:F2} MB > {MaxLogsFolderSizeBytes / 1024.0 / 1024.0:F2} MB). Folder cleaned."; + using (StreamWriter sw = new StreamWriter(Path.Combine(logsPath, $"Log_{AppStartTime}.txt"), true)) + { + sw.WriteLine($"{DateTime.Now:O} [Cleanup] {cleanupMessage}"); + } + } + } + catch { } + } + internal static void WriteLogToFile(string v, object warning) { WriteLogToFile($"[Warning] {v}", LogType.Warning); diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 437ab176..593d449a 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -1693,7 +1693,14 @@ IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold" Toggled="ToggleSwitchIsLogEnabled_Toggled" /> - + + + + + @@ -3641,7 +3648,7 @@ TickFrequency="1" TickPlacement="None" ValueChanged="HighlighterWidthSlider_ValueChanged" /> diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index e19807b1..982e7702 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -1724,6 +1724,12 @@ namespace Ink_Canvas { Settings.Advanced.IsLogEnabled = ToggleSwitchIsLogEnabled.IsOn; SaveSettingsToFile(); } + + private void ToggleSwitchIsSaveLogByDate_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + Settings.Advanced.IsSaveLogByDate = ToggleSwitchIsSaveLogByDate.IsOn; + SaveSettingsToFile(); + } private void ToggleSwitchIsSecondConfimeWhenShutdownApp_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 338888d2..77f45dd9 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -547,41 +547,30 @@ namespace Ink_Canvas { FingerModeBoundsWidthSlider.Value = Settings.Advanced.FingerModeBoundsWidth; NibModeBoundsWidthSlider.Value = Settings.Advanced.NibModeBoundsWidth; ToggleSwitchIsLogEnabled.IsOn = Settings.Advanced.IsLogEnabled; - + ToggleSwitchIsSaveLogByDate.IsOn = Settings.Advanced.IsSaveLogByDate; ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn = Settings.Advanced.IsSecondConfirmWhenShutdownApp; - - ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier; - ToggleSwitchIsSpecialScreen.IsOn = Settings.Advanced.IsSpecialScreen; - - TouchMultiplierSlider.Visibility = - ToggleSwitchIsSpecialScreen.IsOn ? Visibility.Visible : Visibility.Collapsed; - ToggleSwitchIsQuadIR.IsOn = Settings.Advanced.IsQuadIR; - + ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier; ToggleSwitchIsEnableFullScreenHelper.IsOn = Settings.Advanced.IsEnableFullScreenHelper; + ToggleSwitchIsEnableEdgeGestureUtil.IsOn = Settings.Advanced.IsEnableEdgeGestureUtil; + ToggleSwitchIsEnableForceFullScreen.IsOn = Settings.Advanced.IsEnableForceFullScreen; + ToggleSwitchIsEnableResolutionChangeDetection.IsOn = Settings.Advanced.IsEnableResolutionChangeDetection; + ToggleSwitchIsEnableDPIChangeDetection.IsOn = Settings.Advanced.IsEnableDPIChangeDetection; + ToggleSwitchIsEnableAvoidFullScreenHelper.IsOn = Settings.Advanced.IsEnableAvoidFullScreenHelper; if (Settings.Advanced.IsEnableFullScreenHelper) { FullScreenHelper.MarkFullscreenWindowTaskbarList(new WindowInteropHelper(this).Handle, true); } - - ToggleSwitchIsEnableAvoidFullScreenHelper.IsOn = Settings.Advanced.IsEnableAvoidFullScreenHelper; if (Settings.Advanced.IsEnableAvoidFullScreenHelper) { AvoidFullScreenHelper.StartAvoidFullScreen(this); } - - ToggleSwitchIsEnableEdgeGestureUtil.IsOn = Settings.Advanced.IsEnableEdgeGestureUtil; if (Settings.Advanced.IsEnableEdgeGestureUtil) { if (OSVersion.GetOperatingSystem() >= OperatingSystem.Windows10) EdgeGestureUtil.DisableEdgeGestures(new WindowInteropHelper(this).Handle, true); } - - ToggleSwitchIsEnableForceFullScreen.IsOn = Settings.Advanced.IsEnableForceFullScreen; - - ToggleSwitchIsEnableDPIChangeDetection.IsOn = Settings.Advanced.IsEnableDPIChangeDetection; - - ToggleSwitchIsEnableResolutionChangeDetection.IsOn = - Settings.Advanced.IsEnableResolutionChangeDetection; + TouchMultiplierSlider.Visibility = + ToggleSwitchIsSpecialScreen.IsOn ? Visibility.Visible : Visibility.Collapsed; } else { Settings.Advanced = new Advanced(); } diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 51ebfcd0..522e90f1 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -381,6 +381,9 @@ namespace Ink_Canvas [JsonProperty("isLogEnabled")] public bool IsLogEnabled { get; set; } = true; + + [JsonProperty("isSaveLogByDate")] + public bool IsSaveLogByDate { get; set; } = true; [JsonProperty("isEnableFullScreenHelper")] public bool IsEnableFullScreenHelper { get; set; } = false; From 6da13c4fc0d3e61f58bb28408d6a58ba04ec37d8 Mon Sep 17 00:00:00 2001 From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com> Date: Sun, 29 Jun 2025 13:01:33 +0800 Subject: [PATCH 3/6] =?UTF-8?q?add:=E7=9B=B4=E6=8E=A5=E8=B0=83Ci=E7=82=B9?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/MainWindow.xaml | 9 +++++++++ .../MainWindow_cs/MW_FloatingBarIcons.cs | 20 ++++++++++++++++++- Ink Canvas/MainWindow_cs/MW_Settings.cs | 10 ++++++++++ Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 2 ++ Ink Canvas/Resources/Settings.cs | 2 ++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 593d449a..2ebb8efb 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2418,6 +2418,15 @@ FontWeight="Bold" Toggled="ToggleSwitchShowRandomAndSingleDraw_Toggled" /> + + + + diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs index e74d36a6..03ee2b5e 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs @@ -23,6 +23,7 @@ using System.Text; using System.Globalization; using System.Windows.Data; using System.Xml.Linq; +using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox; namespace Ink_Canvas { public partial class MainWindow : Window { @@ -825,7 +826,24 @@ namespace Ink_Canvas { AnimationsHelper.HideWithSlideAndFade(BorderTools); AnimationsHelper.HideWithSlideAndFade(BoardBorderTools); - new RandWindow(Settings, true).ShowDialog(); + // 检查是否启用了直接调用ClassIsland点名功能 + if (Settings.RandSettings.DirectCallCiRand) { + try { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { + FileName = "classisland://plugins/IslandCaller/Run", + UseShellExecute = true + }); + } + catch (Exception ex) { + MessageBox.Show("无法调用ClassIsland点名:" + ex.Message); + + // 调用失败时回退到默认的随机点名窗口 + new RandWindow(Settings, true).ShowDialog(); + } + } else { + // 使用默认的随机点名窗口 + new RandWindow(Settings, true).ShowDialog(); + } } private void GridInkReplayButton_MouseUp(object sender, MouseButtonEventArgs e) { diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 982e7702..099212cf 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -1775,6 +1775,16 @@ namespace Ink_Canvas { // 保存设置到文件 SaveSettingsToFile(); } + + private void ToggleSwitchDirectCallCiRand_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + + // 获取开关状态并保存到设置中 + Settings.RandSettings.DirectCallCiRand = ToggleSwitchDirectCallCiRand.IsOn; + + // 保存设置到文件 + SaveSettingsToFile(); + } #endregion diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 77f45dd9..d36a7f9d 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -602,6 +602,7 @@ namespace Ink_Canvas { RandWindowOnceCloseLatencySlider.Value = Settings.RandSettings.RandWindowOnceCloseLatency; RandWindowOnceMaxStudentsSlider.Value = Settings.RandSettings.RandWindowOnceMaxStudents; ToggleSwitchShowRandomAndSingleDraw.IsOn = Settings.RandSettings.ShowRandomAndSingleDraw; + ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand; RandomDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed; SingleDrawPanel.Visibility = Settings.RandSettings.ShowRandomAndSingleDraw ? Visibility.Visible : Visibility.Collapsed; } else { @@ -609,6 +610,7 @@ namespace Ink_Canvas { ToggleSwitchDisplayRandWindowNamesInputBtn.IsOn = Settings.RandSettings.DisplayRandWindowNamesInputBtn; RandWindowOnceCloseLatencySlider.Value = Settings.RandSettings.RandWindowOnceCloseLatency; RandWindowOnceMaxStudentsSlider.Value = Settings.RandSettings.RandWindowOnceMaxStudents; + ToggleSwitchDirectCallCiRand.IsOn = Settings.RandSettings.DirectCallCiRand; } // Automation diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 522e90f1..a5abddbc 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -437,5 +437,7 @@ namespace Ink_Canvas public int RandWindowOnceMaxStudents { get; set; } = 10; [JsonProperty("showRandomAndSingleDraw")] public bool ShowRandomAndSingleDraw { get; set; } = true; + [JsonProperty("directCallCiRand")] + public bool DirectCallCiRand { get; set; } = false; } } \ No newline at end of file From 60c6e0632df2e9863b0f89e25fe3aa19c0e785a2 Mon Sep 17 00:00:00 2001 From: CJK_mkp <113243675+CJKmkp@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:15:20 +0800 Subject: [PATCH 4/6] =?UTF-8?q?add:=E9=AB=98=E7=B2=BE=E5=BA=A6=E7=9B=B4?= =?UTF-8?q?=E7=BA=BF=E6=8B=89=E7=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/MainWindow.xaml | 10 +- Ink Canvas/MainWindow.xaml.cs | 2 +- Ink Canvas/MainWindow_cs/MW_Settings.cs | 8 + Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 2 + .../MW_SimulatePressure&InkToShape.cs | 169 ++++++++++++++++-- Ink Canvas/Resources/Settings.cs | 2 + 6 files changed, 179 insertions(+), 14 deletions(-) diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 2ebb8efb..546a061f 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -970,7 +970,15 @@ - + + + + + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index c9b41a61..98de85fd 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -395,7 +395,7 @@ namespace Ink_Canvas { if (string.IsNullOrEmpty(releaseNotes)) { releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新 - + 无法获取更新日志,但新版本已准备就绪。"; } diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 099212cf..074e9dd6 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -782,6 +782,14 @@ namespace Ink_Canvas { // 立即保存设置到文件,确保设置不会丢失 SaveSettingsToFile(); } + + private void ToggleSwitchHighPrecisionLineStraighten_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + + Settings.Canvas.HighPrecisionLineStraighten = ToggleSwitchHighPrecisionLineStraighten.IsOn; + System.Diagnostics.Debug.WriteLine($"HighPrecisionLineStraighten changed: {Settings.Canvas.HighPrecisionLineStraighten}"); + SaveSettingsToFile(); + } #endregion diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index d36a7f9d..100de955 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -533,6 +533,8 @@ namespace Ink_Canvas { AutoStraightenLineThresholdSlider.Value = Settings.Canvas.AutoStraightenLineThreshold; // 直线拉直灵敏度也在这里初始化,即使它存储在InkToShape中 LineStraightenSensitivitySlider.Value = Settings.InkToShape.LineStraightenSensitivity; + // 初始化高精度直线拉直设置 + ToggleSwitchHighPrecisionLineStraighten.IsOn = Settings.Canvas.HighPrecisionLineStraighten; // 初始化直线端点吸附相关设置 ToggleSwitchLineEndpointSnapping.IsOn = Settings.Canvas.LineEndpointSnapping; diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index e1a386ca..bb67682a 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -687,20 +687,97 @@ namespace Ink_Canvas { double totalDeviation = 0; int pointCount = 0; - // Calculate deviation for each point - foreach (StylusPoint sp in stroke.StylusPoints) { - Point p = sp.ToPoint(); - double deviation = DistanceFromLineToPoint(start, end, p); - maxDeviation = Math.Max(maxDeviation, deviation); - totalDeviation += deviation; - pointCount++; + // 检查是否启用了高精度直线拉直 + bool useHighPrecision = Settings.Canvas.HighPrecisionLineStraighten; + + if (useHighPrecision) { + System.Diagnostics.Debug.WriteLine("使用高精度直线拉直模式"); + + // 高精度模式:每隔10像素取一个计数点 + double strokeLength = 0; + double sampleInterval = 10.0; // 10像素间隔 + + // 计算笔画的总长度,用于后续采样 + for (int i = 1; i < stroke.StylusPoints.Count; i++) { + Point p1 = stroke.StylusPoints[i-1].ToPoint(); + Point p2 = stroke.StylusPoints[i].ToPoint(); + strokeLength += GetDistance(p1, p2); + } + + // 如果笔画太短,直接使用所有点 + if (strokeLength < sampleInterval * 5) { + foreach (StylusPoint sp in stroke.StylusPoints) { + Point p = sp.ToPoint(); + double deviation = DistanceFromLineToPoint(start, end, p); + maxDeviation = Math.Max(maxDeviation, deviation); + totalDeviation += deviation; + pointCount++; + } + } else { + // 使用等距采样点 + double currentLength = 0; + double nextSampleAt = 0; + + // 总是包含起点 + Point lastPoint = start; + double deviation = DistanceFromLineToPoint(start, end, lastPoint); + maxDeviation = Math.Max(maxDeviation, deviation); + totalDeviation += deviation; + pointCount++; + + // 采样中间点 + for (int i = 1; i < stroke.StylusPoints.Count; i++) { + Point currentPoint = stroke.StylusPoints[i].ToPoint(); + double segmentLength = GetDistance(lastPoint, currentPoint); + + // 如果这段线段跨越了下一个采样点 + while (currentLength + segmentLength >= nextSampleAt) { + // 计算采样点在线段上的位置 + double t = (nextSampleAt - currentLength) / segmentLength; + Point samplePoint = new Point( + lastPoint.X + t * (currentPoint.X - lastPoint.X), + lastPoint.Y + t * (currentPoint.Y - lastPoint.Y) + ); + + // 计算采样点的偏差 + deviation = DistanceFromLineToPoint(start, end, samplePoint); + maxDeviation = Math.Max(maxDeviation, deviation); + totalDeviation += deviation; + pointCount++; + + // 设置下一个采样点位置 + nextSampleAt += sampleInterval; + + // 防止无限循环 + if (nextSampleAt > strokeLength) break; + } + + currentLength += segmentLength; + lastPoint = currentPoint; + } + + // 总是包含终点 + deviation = DistanceFromLineToPoint(start, end, end); + maxDeviation = Math.Max(maxDeviation, deviation); + totalDeviation += deviation; + pointCount++; + } + } else { + // 原始模式:使用所有点 + foreach (StylusPoint sp in stroke.StylusPoints) { + Point p = sp.ToPoint(); + double deviation = DistanceFromLineToPoint(start, end, p); + maxDeviation = Math.Max(maxDeviation, deviation); + totalDeviation += deviation; + pointCount++; + } } // 计算平均偏差 double avgDeviation = totalDeviation / pointCount; // 更详细的调试信息 - System.Diagnostics.Debug.WriteLine($"Max deviation: {maxDeviation}, Avg: {avgDeviation}, Threshold: {sensitivity * lineLength}"); + System.Diagnostics.Debug.WriteLine($"Max deviation: {maxDeviation}, Avg: {avgDeviation}, Threshold: {sensitivity * lineLength}, Points: {pointCount}"); // 支持更广泛的灵敏度范围 (0.05-2.0) @@ -722,11 +799,79 @@ namespace Ink_Canvas { else { // 检查点分布的一致性 - 如果有些点偏离很大而其他点很接近直线,表明线条有明显弯曲 double deviationVariance = 0; - foreach (StylusPoint sp in stroke.StylusPoints) { - Point p = sp.ToPoint(); - double deviation = DistanceFromLineToPoint(start, end, p); - deviationVariance += Math.Pow(deviation - avgDeviation, 2); + + // 使用相同的高精度/原始模式来计算方差 + if (useHighPrecision) { + // 高精度模式:重新采样计算方差 + double strokeLength = 0; + double sampleInterval = 10.0; // 10像素间隔 + + // 计算笔画的总长度,用于后续采样 + for (int i = 1; i < stroke.StylusPoints.Count; i++) { + Point p1 = stroke.StylusPoints[i-1].ToPoint(); + Point p2 = stroke.StylusPoints[i].ToPoint(); + strokeLength += GetDistance(p1, p2); + } + + // 如果笔画太短,直接使用所有点 + if (strokeLength < sampleInterval * 5) { + foreach (StylusPoint sp in stroke.StylusPoints) { + Point p = sp.ToPoint(); + double deviation = DistanceFromLineToPoint(start, end, p); + deviationVariance += Math.Pow(deviation - avgDeviation, 2); + } + } else { + // 使用等距采样点 + double currentLength = 0; + double nextSampleAt = 0; + Point lastPoint = start; + + // 起点方差 + double deviation = DistanceFromLineToPoint(start, end, lastPoint); + deviationVariance += Math.Pow(deviation - avgDeviation, 2); + + // 采样中间点 + for (int i = 1; i < stroke.StylusPoints.Count; i++) { + Point currentPoint = stroke.StylusPoints[i].ToPoint(); + double segmentLength = GetDistance(lastPoint, currentPoint); + + // 如果这段线段跨越了下一个采样点 + while (currentLength + segmentLength >= nextSampleAt) { + // 计算采样点在线段上的位置 + double t = (nextSampleAt - currentLength) / segmentLength; + Point samplePoint = new Point( + lastPoint.X + t * (currentPoint.X - lastPoint.X), + lastPoint.Y + t * (currentPoint.Y - lastPoint.Y) + ); + + // 计算采样点的方差 + deviation = DistanceFromLineToPoint(start, end, samplePoint); + deviationVariance += Math.Pow(deviation - avgDeviation, 2); + + // 设置下一个采样点位置 + nextSampleAt += sampleInterval; + + // 防止无限循环 + if (nextSampleAt > strokeLength) break; + } + + currentLength += segmentLength; + lastPoint = currentPoint; + } + + // 终点方差 + deviation = DistanceFromLineToPoint(start, end, end); + deviationVariance += Math.Pow(deviation - avgDeviation, 2); + } + } else { + // 原始模式:使用所有点计算方差 + foreach (StylusPoint sp in stroke.StylusPoints) { + Point p = sp.ToPoint(); + double deviation = DistanceFromLineToPoint(start, end, p); + deviationVariance += Math.Pow(deviation - avgDeviation, 2); + } } + deviationVariance /= pointCount; // 输出更多调试信息 diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index a5abddbc..9513fe39 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -56,6 +56,8 @@ namespace Ink_Canvas public bool AutoStraightenLine { get; set; } = true; // 是否启用直线自动拉直 [JsonProperty("autoStraightenLineThreshold")] public int AutoStraightenLineThreshold { get; set; } = 30; // 直线自动拉直的长度阈值(像素) + [JsonProperty("highPrecisionLineStraighten")] + public bool HighPrecisionLineStraighten { get; set; } = true; // 是否启用高精度直线拉直 [JsonProperty("lineEndpointSnapping")] public bool LineEndpointSnapping { get; set; } = true; // 是否启用直线端点吸附 [JsonProperty("lineEndpointSnappingThreshold")] From e0e22a09c0d100178ab5cf4094afbfd442de82f6 Mon Sep 17 00:00:00 2001 From: Hydrogen Date: Sun, 29 Jun 2025 14:52:19 +0800 Subject: [PATCH 5/6] Update gesture icon image Replaces the existing gesture.png icon in Resources/new-icons with a new version. --- Ink Canvas/Resources/new-icons/gesture.png | Bin 1968 -> 2577 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Ink Canvas/Resources/new-icons/gesture.png b/Ink Canvas/Resources/new-icons/gesture.png index c0b726005edbd7cc5c60f4154c584d54fae190a7..8e7c6401916b296951efe2e2a8dcfc6362542a9a 100644 GIT binary patch literal 2577 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 F{TJZkYa{>w delta 1926 zcmV;12YL9B6tEAFBoYa5NLh0L04z%Y04z%Zr9GCZks%*{2SrIlK~#7F?VZh1+ei?{ z+lC}mLV}Nc)`}-sc!D*zy#cq});vMrwl~ZZK#n=tc>>@GP;S{1aNC?vs`x6PYR#96 z<#Yc{#v8?%k&QLdj9`9MF3FaxkM5c7o}L+r$l-7}91e%W;cz${j;BP9GS1M@&=?kD zxm>RA?(Xh?80#B5J3AYc5i+X)2!0lecldac%>h1TDB>eoPUaNgd0ye_>S`IQ#a8>G zMy%B9^_dzVOUYBlfbiF-4dF8|OH;32y;>%V>7ooVJUm>+;w?Q4ueZ1N`0VWLGg(Nc zOc3n;7RhaXD{ANG=c6{7qRg0}k&%%pQV1WmKER@XDwZ+|Fh!GOA(>IY?d|Pvvfo+g zaJ1uycxHBYcSrG)ulWBMSxARrvSrr zlcjin;QIR7YkWtVnc@;YW_Ne@Mi2zXQ`gohK#cd9yt=w7l3q$UW~uQ7>ohnxxRJ}{ zmQ0Qxvc?2)>4fmtvARfY4m*hpgwNfSRqpKP^OFIrPypY&81G}M-}e6eCX`@>3Bt|K zQ)k0XbmCoJUM>$056{8h@Cb?mmIc(30P7on&z?Q|1fLO*W{fKUH(bPxmdFCwB;i`t zLf`-2#y$hry0E>yT_%kfH$k}JKO#1>lUyOa@W^SOIsn%?568V;0Hle;Y5)(SMaah` z%ERXu2=XgeXOtknX;-P;$A8MZySod#-rwKvA#7Y{lR)tpZwy_BAm?#CWqKf7OJ2Tz zd|9d0YSW|v?KOY}&to_t{4G(PE8<2g{GB5Xd@2oxrxGfv{Ay!;e}6yVkX z`Sa)RsY7_s@4&#o6lp-K0!BtgN<#4S5vA3mZ!!5=)J>qo|3eyS9xoKP5th&A-;oBi zDS(A}dwW}XfSK@c6U!(~9;E=z)3(5WdAQ@v%}oiPVuTMW>hJ|xe1M4K7wM6Jdzn5w zJiIqoxV25x>Y}V*_4M@oip3|2ftDIzNgS_@u=!(C1>q+)=S)itV5bz3Ec5BxoGp)1 zN-<=K*a{F0DU~f84@VxnFBZ!khLcho-t1gadTZQhY6kx4r~k8GkX_5(-riS#N{Pf8 zVjMO$PZ#Aj00cDg5Qo?R|5=rsBY!@0BM8k)(-cI3hNE<5CWomg0A`~g(MkYzk5}118a;0M4sK%up;xwHR3mFh%1~ z!YqGyIw?xO?hL54Xql*u%;3eFH*fes8M53?0i78ar>Cb%TX}YNRy#R=Ir)n}7l14S z_f{fFLz5#WTzf22sVxdZ$XO*to}Ej!&K&wMBo)D7X+$lkIEVb5L=+%$=qiWo zp;KE+civ+lv1A;=QZ_?>(vFM_hNX#JYog(`!`o7Ifh z*VmWBK0X;nOceZ6vDrtD-2!PM^$8kq$a7KI(x^V21#a z3ZSHq*srQn5km0nZ_2F(3!~)E!tr)Ir6~&IkFRfKcblkxLKv$rUcB%-nPL>0I5-KV zmfAUIc(Py;Sn>V~w7fJyuUtuCl5>KQqQA(s)>cd#6 zfCeE&aEzX5RLa^%r%^d=!8fp40bvFQ2f1TpO5~B^_z8B}_s^h!;8#q~L&%H*!f*!; zI`usWu#Y09$D-}PVds66#BbW*$myv}{BSrN4u`|xa5x+ehr>?(17(4%Z0MIqBme*a M07*qoM6N<$f`R{;6aWAK From 831fd096153d675afb412154c75765877cc5c54d Mon Sep 17 00:00:00 2001 From: Hydrogen Date: Sun, 29 Jun 2025 15:01:42 +0800 Subject: [PATCH 6/6] Update gesture-enabled icon Replaces the existing gesture-enabled.png icon with a new version in the Resources/new-icons directory. --- .../Resources/new-icons/gesture-enabled.png | Bin 2058 -> 3783 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Ink Canvas/Resources/new-icons/gesture-enabled.png b/Ink Canvas/Resources/new-icons/gesture-enabled.png index afbf60d6cf797910f1085603aea902d0970e18f4..fe0b9b374c6c52d4f2da6b62bf56d1bc3f9c61f8 100644 GIT binary patch delta 3758 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 delta 2017 zcmV<72Ojvx9f}Z;BoYa5NLh0L04z%Y04z%Zr9GCZks%*{2cJnqK~#7F?VVjx+ei?{ zyGOQ(fmECmtaE~O-gbf1niBvgh&e&Xb9RvxYM(<+0678S1UR+CN$ozwIYFEggj5K| zADZ47*(L^$gt13oVSZHAaV3xGWli^Z$kh@gpp)wg#36(pS02~f7F(9N75h*mCN zw=V&_&bjT)nsfgI5=sVAMwhjYM9Y>Y*;ia$>s>Zi5R0pjP#C0$73X_ZwCY5Se*r@I zcW<~#CaDsD5Ff)7FhIL(eIFzUMu3q3FAZCmJF=GUX86!Z<7y)9&2oN(yfS54?#EcOj zW{d!TF=GUXnZ(|d|IKa{Gnrm-__w(6qV+Q~y~Re&!PrCBi4(xf_d#n-rkxO3BU@%u z0D&r&1(o4vcC8jH@0>M&<8GY!Lx~ZfEUwNjqXV&6(sH>^f=q8g+Fx?cs+{BMdnez7 zh)IME@EYHuqZ_B3XaW(6jM0%Cq99@t(F3}F7TDmE@embK1WWY5m7Ug8B3vV&g6JIu zB-Ru?vf*-g6Gjh-T>$Y9i?N<6kOV{z6z6i}-NKiDdm|zDQtX8NfEDMPb8O$Of<|H= zg2Zs|pKL|YkoldU^$Vflo4Rt{p=jC5&e;c@NnWA#0GUMF%bb@uxkYjP`~u*9W=PF{ zwE-MR0jt}j(vHZrQhO%c7MW6kY} zr47)4_8uS`UgmIit+P+EJ0q|?;R06WZ{Y$S36JB`((~5dg~20GHfopXaJXD^?l;A$ z&ejtMYQx5teTePnx?dNZScRe&Y?wBj{9qV0FJN@FK|a_Myh{S(>j!WYm8 zAzYet&6={#89o6GXcIs-CR%K7(qTdh!7Y@2e-R>K@>#D;cN8aB;gl`=+8~6o4qKqb z1`Pc87x0tN<$U8myIAN$}%!GCKTuLlv@r&}z;! zc|m;Lx7tYn7C)HIoh0QLPMlv_&zW_C zZf|TqR30zajh{>XPy7rK5!ysd8n4mDmmE9!8o9LU*O)<|i-%+1A&e#GosXmI>d$iW zJDXSc?LuaM?08_io2gD$Alz$`5zl)nn(%%y)p8 zGnCN6BmcsSd@;(QAc*B1S(!O*zc^v}ot=-@3mQZSo2qTZEZgt6pq?OqhnA!?e%NRk zf(8Y09%^8h(nS$clz$bj3`Z8GH0v2rqQ;shMu6u+hpj-?rGUQm5|ao;b_E-d|4 zQVXTyMynf&-0v#KLdUv)^V7tNbfQ%N>8tzq0;;Uo-^yUPg!YM8mY;9>h7AJT(%Xu5 z0i;1yP4PX1TWWg2WkLx>^xaZ*FA%}9J!6o65Q+f(+kL1s$Rlp40YVJMz1^PQy$GCL zA$@Ksg?6r?<^>@~pO69;x;U2}gG^LZt?qI6E6hmf5A74Npd-J3J7Gc5(xfoB->r=H zie5f*`-Pqv*LO?Rq`@uiSZ-Olfk?}J;?M=?yQNrFZWL~*t^}vhz>M-yXM~Ur0VF|g zsmt(1QVp^kevr8G0zqOm&_E;tq}46$F<$Z8CWh8S&_JY5&J8liu8R-is8I3aqG&|* zXk>BYzP`P%2^xrhG(FuvS_)t>+JgiUlx#-C_CXVo3gAUc{n9>UoXGiBg#^%lFt|_A z2tjSu8}Xw=UZ9cn(ms5mrCtFOy8~h^xWZJ?>&=3p5hB06_-X1Nm-TuIaU@ zGNSjY@{496mJ+3YoXD?C2n#Z8J?aN;_tWbG8eeANxg7_8G!xtV1ElZYwdOuRKvuO( zci}ELVR}biDHZ!(C1ioF@YOt5UBvnVhe>L`Gy;= z<)p_K^h#KNo5Ge%Bm{cZm@=$9d1`!^SN@R1yhFwlfz2Jn^|*pj2Ja)N==AsfNQ?kO zrmo-Bg_vk%d3z(O7yl$q0Lj2O%Tm`0L`0NV{V0hPV8{<4V12r>rU?*NJQw#XqHg#h zi56g#C8xcH*jpDyw=|ob5MdC)250aZG5