From 3f8eeda65b5f468f2c9a83d6ed6a5a05c60c433b Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Fri, 10 Mar 2023 17:15:55 -0800 Subject: [PATCH] integrating offline changes --- Launcher/Launcher.csproj | 2 +- MatterControl.Printing/GCode/GCodeFile.cs | 2 +- .../Settings/IObjectSlicer.cs | 2 +- MatterControl.SLA/Slicer/SlaSlicer.cs | 2 +- .../MatterControl.Winforms.csproj | 2 +- MatterControl.csproj | 4 +- .../ApplicationView/Application.cs | 8 +- .../ApplicationView/ApplicationController.cs | 49 +- .../ApplicationView/Config/BedConfig.cs | 25 +- .../ApplicationView/Config/PrinterConfig.cs | 22 +- .../Config/RunningTasksConfig.cs | 77 +- .../ApplicationView/ISceneContext.cs | 2 +- .../RunningTasks/RunningTaskDetails.cs | 14 +- .../ApplicationView/SceneOperations.cs | 13 +- .../ColorPicker/RadialColorPicker.cs | 602 ++++----- .../CustomWidgets/SceneOperation.cs | 4 +- MatterControlLib/DesignTools/Constants.cs | 26 +- .../EditorTools/EditorToolsPlugin.cs | 2 +- MatterControlLib/DesignTools/Expressions.cs | 571 +++++++++ MatterControlLib/DesignTools/Lithophane.cs | 318 ++--- .../DesignTools/Obsolete/CombineObject3D.cs | 204 ++-- .../DesignTools/Obsolete/CurveObject3D.cs | 2 +- .../Obsolete/FitToBoundsObject3D.cs | 2 +- .../Obsolete/IntersectionObject3D.cs | 196 ++- .../DesignTools/Obsolete/PinchObject3D.cs | 2 +- .../DesignTools/Obsolete/RotateObject3D.cs | 2 +- .../DesignTools/Operations/AlignObject3D.cs | 2 +- .../DesignTools/Operations/AlignObject3D_2.cs | 2 +- .../DesignTools/Operations/ArrayObject3D.cs | 150 +-- .../DesignTools/Operations/CurveObject3D_2.cs | 336 +++--- .../DesignTools/Operations/CurveObject3D_3.cs | 573 +++++---- .../Operations/DecimateObject3D.cs | 4 +- .../Operations/FitToBoundsObject3D_3.cs | 2 +- .../Operations/FitToCylinderObject3D.cs | 2 +- .../DesignTools/Operations/Image/Histogram.cs | 4 +- .../Operations/Image/ImageToPathObject3D.cs | 539 ++++----- .../Operations/Image/ImageToPathObject3D_2.cs | 13 +- .../Operations/Image/PathObject3D.cs | 48 +- .../Operations/Path/InflatePathObject3D.cs | 2 +- .../Operations/Path/LinearExtrudeObject3D.cs | 216 ++-- .../Operations/Path/MergePathObject3D.cs | 193 ++- .../Operations/Path/OutlinePathObject3D.cs | 2 +- .../Operations/Path/RevolveObject3D.cs | 4 +- .../Operations/Path/SmoothPathObject3D.cs | 2 +- .../Operations/RotateObject3D_2.cs | 2 +- .../Operations/TranslateObject3D.cs | 2 +- .../DesignTools/Operations/TwistObject3D.cs | 10 +- .../DesignTools/Primitives/BaseObject3D.cs | 2 +- .../DesignTools/Primitives/BoxPathObject3D.cs | 168 +-- .../DesignTools/Primitives/ConeObject3D.cs | 2 +- .../DesignTools/Primitives/CubeObject3D.cs | 8 +- .../Primitives/CylinderObject3D.cs | 2 +- .../Primitives/DescriptionObject3D.cs | 2 +- .../Primitives/DualContouringObject3D.cs | 2 +- .../Primitives/HalfCylinderObject3D.cs | 2 +- .../Primitives/HalfSphereObject3D.cs | 2 +- .../Primitives/HalfWedgeObject3D.cs | 2 +- .../OperationSourceContainerObject3D.cs | 6 +- .../DesignTools/Primitives/PyramidObject3D.cs | 2 +- .../DesignTools/Primitives/QrCodeObject3D.cs | 2 +- .../DesignTools/Primitives/RingObject3D.cs | 2 +- .../Primitives/SetTemperatureObject3D.cs | 2 +- .../DesignTools/Primitives/SphereObject3D.cs | 2 +- .../Primitives/TemperatureTowerObject3D.cs | 6 +- .../DesignTools/Primitives/TextObject3D.cs | 4 +- .../DesignTools/Primitives/TorusObject3D.cs | 2 +- .../DesignTools/Primitives/WedgeObject3D.cs | 2 +- .../DesignTools/Primitives/WedgeObject3D_2.cs | 2 +- .../DesignTools/Sheets/DoubleOrExpression.cs | 134 +- .../DesignTools/Sheets/IntOrExpression.cs | 116 +- .../DesignTools/Sheets/SheetObject3D.cs | 729 ++--------- .../DesignTools/Sheets/StringOrExpression.cs | 46 +- .../DesignTools/SupportGenerator.cs | 20 +- MatterControlLib/HelpIndex.cs | 12 +- MatterControlLib/Library/Export/AmfExport.cs | 2 +- .../Library/Export/FolderExport.cs | 2 +- .../Library/Export/GCodeExport.cs | 24 +- .../Library/Export/IExportPlugin.cs | 2 +- MatterControlLib/Library/Export/McxExport.cs | 2 +- MatterControlLib/Library/Export/MeshExport.cs | 120 +- MatterControlLib/Library/Export/StlExport.cs | 2 +- MatterControlLib/Library/Export/ZipExport.cs | 2 +- .../FileSystem/FileSystemContainer.cs | 841 +++++++------ .../Providers/FileSystem/FileSystemItem.cs | 2 +- .../Providers/GitHub/GitHubContainer.cs | 2 +- .../Providers/GitHub/GitHubLibraryItem.cs | 2 +- .../CalibrationPartsContainer.cs | 2 +- .../DesignAppsCollectionContainer.cs | 43 +- .../MatterControl/Primitives2DContainer.cs | 100 ++ .../MatterControl/PrimitivesContainer.cs | 69 +- .../MatterControl/ScriptingPartsContainer.cs | 2 +- .../Library/Providers/Zip/ZipMemoryItem.cs | 2 +- MatterControlLib/Library/RemoteLibraryItem.cs | 2 +- .../Library/Widgets/AddPrinterWidget.cs | 2 +- .../Library/Widgets/LibraryWidget.cs | 9 +- .../Widgets/ListView/LibraryListView.cs | 5 +- MatterControlLib/MatterControlLib.csproj | 10 +- .../LeftClipFlowLayoutWidget.cs | 2 +- .../PartPreviewWindow/RunningTaskRow.cs | 6 +- .../RunningTaskStatusPanel.cs | 29 +- .../PartPreviewWindow/SelectedObjectPanel.cs | 1075 +++++++++-------- .../View3D/Actions/CombineObject3D_2.cs | 217 ++-- .../View3D/Actions/IntersectionObject3D_2.cs | 278 +++-- .../View3D/Actions/SheetEditor.cs | 2 +- .../Actions/SubtractAndReplaceObject3D.cs | 306 +++-- .../Actions/SubtractAndReplaceObject3D_2.cs | 468 ++++--- .../View3D/Actions/SubtractObject3D.cs | 243 ++-- .../View3D/Actions/SubtractObject3D_2.cs | 506 ++++---- .../View3D/Actions/SubtractPathObject3D.cs | 372 +++--- .../PartPreviewWindow/View3D/SceneActions.cs | 17 +- .../View3D/SliceProgressReporter.cs | 19 +- .../Drivers/X3G/X3GExport.cs | 2 +- .../Io/ValidatePrintLevelingStream.cs | 11 +- .../SettingsManagement/OemSettings.cs | 1 - .../EngineMappingsMatterSlice.cs | 24 +- .../Settings/ProfileManager.cs | 2 +- .../SlicePresetsWindow/SlicePresetsPage.cs | 2 +- .../SlicerConfiguration/Slicer.cs | 2 +- .../PartsSheetCreator.cs | 471 ++++---- PrinterDriverInstaller/InfInstaller.csproj | 2 +- StaticData/Translations/Master.txt | 6 + Submodules/MatterSlice | 2 +- Submodules/agg-sharp | 2 +- .../MatterControl.AutomationTests.csproj | 8 +- .../MatterControl.Tests.csproj | 8 +- 125 files changed, 5442 insertions(+), 5434 deletions(-) create mode 100644 MatterControlLib/DesignTools/Expressions.cs create mode 100644 MatterControlLib/Library/Providers/MatterControl/Primitives2DContainer.cs diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj index 2cf96e379..b325753a1 100644 --- a/Launcher/Launcher.csproj +++ b/Launcher/Launcher.csproj @@ -8,7 +8,7 @@ True - + all diff --git a/MatterControl.Printing/GCode/GCodeFile.cs b/MatterControl.Printing/GCode/GCodeFile.cs index 330a64979..786516d89 100644 --- a/MatterControl.Printing/GCode/GCodeFile.cs +++ b/MatterControl.Printing/GCode/GCodeFile.cs @@ -227,7 +227,7 @@ namespace MatterControl.Printing && (stopPos == -1 || stringPos < stopPos || string.IsNullOrEmpty(stopCheckingString))) { stringPos += stringToCheckAfter.Length; - readValue = agg_basics.ParseDouble(stringWithNumber, ref stringPos, true); + readValue = Util.ParseDouble(stringWithNumber, ref stringPos, true); numberEnd = stringPos; return true; diff --git a/MatterControl.Printing/Settings/IObjectSlicer.cs b/MatterControl.Printing/Settings/IObjectSlicer.cs index c8ad6f4f7..f8e9051ae 100644 --- a/MatterControl.Printing/Settings/IObjectSlicer.cs +++ b/MatterControl.Printing/Settings/IObjectSlicer.cs @@ -44,7 +44,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration public interface IObjectSlicer { - Task Slice(IEnumerable itemsOnBed, PrinterSettings printerSettings, string filePath, IProgress progressReporter, CancellationToken cancellationToken); + Task Slice(IEnumerable itemsOnBed, PrinterSettings printerSettings, string filePath, Action progressReporter, CancellationToken cancellationToken); Dictionary Exports { get; } diff --git a/MatterControl.SLA/Slicer/SlaSlicer.cs b/MatterControl.SLA/Slicer/SlaSlicer.cs index 49fd7282b..288b7e671 100644 --- a/MatterControl.SLA/Slicer/SlaSlicer.cs +++ b/MatterControl.SLA/Slicer/SlaSlicer.cs @@ -42,7 +42,7 @@ namespace MatterHackers.gsBundle { public class SlaSlicer : IObjectSlicer { - public async Task Slice(IEnumerable printableItems, PrinterSettings printerSettings, string filePath, IProgress progressReporter, CancellationToken cancellationToken) + public async Task Slice(IEnumerable printableItems, PrinterSettings printerSettings, string filePath, Action progressReporter, CancellationToken cancellationToken) { using (var outputStream = File.OpenWrite(filePath)) { diff --git a/MatterControl.Winforms/MatterControl.Winforms.csproj b/MatterControl.Winforms/MatterControl.Winforms.csproj index 727791770..379e21c21 100644 --- a/MatterControl.Winforms/MatterControl.Winforms.csproj +++ b/MatterControl.Winforms/MatterControl.Winforms.csproj @@ -28,7 +28,7 @@ - + all diff --git a/MatterControl.csproj b/MatterControl.csproj index c97edc301..93b6493a2 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -99,12 +99,12 @@ - + 1.0.2-beta1 - + all diff --git a/MatterControlLib/ApplicationView/Application.cs b/MatterControlLib/ApplicationView/Application.cs index 6a40b1356..ed3a44553 100644 --- a/MatterControlLib/ApplicationView/Application.cs +++ b/MatterControlLib/ApplicationView/Application.cs @@ -714,20 +714,14 @@ namespace MatterHackers.MatterControl null, (progress, cancellationToken) => { - var status = new ProgressStatus(); - int itemCount = ApplicationController.StartupActions.Count; double i = 1; foreach (var action in ApplicationController.StartupActions.OrderByDescending(t => t.Priority)) { - status.Status = action.Title; - progress.Report(status); - action.Action?.Invoke(); - status.Progress0To1 = i++ / itemCount; - progress.Report(status); + progress?.Invoke(i++ / (double)itemCount, action.Title); } return Task.CompletedTask; diff --git a/MatterControlLib/ApplicationView/ApplicationController.cs b/MatterControlLib/ApplicationView/ApplicationController.cs index 10676da47..71e6ff818 100644 --- a/MatterControlLib/ApplicationView/ApplicationController.cs +++ b/MatterControlLib/ApplicationView/ApplicationController.cs @@ -650,7 +650,7 @@ namespace MatterHackers.MatterControl public static Func> GetPrinterProfileAsync; // Executes the user printer profile sync logic in the webservices plugin - public static Func, Task> SyncCloudProfiles; + public static Func, Task> SyncCloudProfiles; public static Action QueueCloudProfileSync; @@ -1223,12 +1223,10 @@ namespace MatterHackers.MatterControl (progress, cancellationToken) => { var time = UiThread.CurrentTimerMs; - var status = new ProgressStatus(); while (UiThread.CurrentTimerMs < time + durationSeconds * 1000) { Thread.Sleep(30); - status.Progress0To1 = (UiThread.CurrentTimerMs - time) / 1000.0 / durationSeconds; - progress.Report(status); + progress?.Invoke((UiThread.CurrentTimerMs - time) / 1000.0 / durationSeconds, null); } return Task.CompletedTask; @@ -1289,21 +1287,20 @@ namespace MatterHackers.MatterControl var paused = false; Tasks.Execute("", printerConnection.Printer, (reporter, cancellationToken) => { - var progressStatus = new ProgressStatus(); - while (printerConnection.SecondsToHoldTemperature > 0 && !cancellationToken.IsCancellationRequested && printerConnection.ContinueHoldingTemperature) { + var status = ""; if (paused) { - progressStatus.Status = "Holding Temperature".Localize(); + status = "Holding Temperature".Localize(); } else { if (printerConnection.SecondsToHoldTemperature > 60) { - progressStatus.Status = string.Format( + status = string.Format( "{0} {1:0}m {2:0}s", "Automatic Heater Shutdown in".Localize(), (int)printerConnection.SecondsToHoldTemperature / 60, @@ -1311,15 +1308,15 @@ namespace MatterHackers.MatterControl } else { - progressStatus.Status = string.Format( + status = string.Format( "{0} {1:0}s", "Automatic Heater Shutdown in".Localize(), printerConnection.SecondsToHoldTemperature); } } - progressStatus.Progress0To1 = printerConnection.SecondsToHoldTemperature / printerConnection.TimeToHoldTemperature; - reporter.Report(progressStatus); + var progress = printerConnection.SecondsToHoldTemperature / printerConnection.TimeToHoldTemperature; + reporter?.Invoke(progress, status); Thread.Sleep(20); } @@ -1869,7 +1866,6 @@ namespace MatterHackers.MatterControl null, async (reporter, cancellationTokenSource) => { - var progressStatus = new ProgressStatus(); for (int i=0; i reporter, CancellationToken cancellationToken, PrinterConnection.PrintingModes printingMode) + public async Task PrintPart(EditContext editContext, PrinterConfig printerConfig, Action reporter, CancellationToken cancellationToken, PrinterConnection.PrintingModes printingMode) { var partFilePath = editContext.SourceFilePath; var gcodeFilePath = await editContext.GCodeFilePath(printerConfig); @@ -2248,9 +2244,6 @@ namespace MatterHackers.MatterControl printer, (reporterB, cancellationTokenB) => { - var progressStatus = new ProgressStatus(); - reporterB.Report(progressStatus); - return Task.Run(() => { string printing = "Printing".Localize(); @@ -2267,10 +2260,10 @@ namespace MatterHackers.MatterControl && !cancellationTokenB.IsCancellationRequested) { var layerCount = printer.Bed.LoadedGCode == null ? "?" : printer.Bed.LoadedGCode.LayerCount.ToString(); - progressStatus.Status = $"{printing} ({printer.Connection.CurrentlyPrintingLayer + 1} of {layerCount}) - {printer.Connection.PercentComplete:0}%"; + var status = $"{printing} ({printer.Connection.CurrentlyPrintingLayer + 1} of {layerCount}) - {printer.Connection.PercentComplete:0}%"; - progressStatus.Progress0To1 = printer.Connection.PercentComplete / 100; - reporterB.Report(progressStatus); + var progress0To1 = printer.Connection.PercentComplete / 100; + reporterB?.Invoke(progress0To1, status); Thread.Sleep(200); } }); @@ -2448,18 +2441,14 @@ namespace MatterHackers.MatterControl await this.Tasks.Execute("Loading GCode".Localize(), printer, (innerProgress, concelationTokenSource) => { - var status = new ProgressStatus(); - - innerProgress.Report(status); - printer.Bed.LoadActiveSceneGCode(gcodeFilePath, concelationTokenSource.Token, (progress0to1, statusText) => { UiThread.RunOnIdle(() => { - status.Progress0To1 = progress0to1; - status.Status = statusText; + var progress0To1 = progress0to1; + var status = statusText; - innerProgress.Report(status); + innerProgress?.Invoke(progress0To1, status); }); }); @@ -2667,7 +2656,7 @@ namespace MatterHackers.MatterControl public int Priority { get; set; } - public Func, CancellationTokenSource, Task> Action { get; set; } + public Func, CancellationTokenSource, Task> Action { get; set; } } public class StartupAction diff --git a/MatterControlLib/ApplicationView/Config/BedConfig.cs b/MatterControlLib/ApplicationView/Config/BedConfig.cs index ba3b7eac9..06354122f 100644 --- a/MatterControlLib/ApplicationView/Config/BedConfig.cs +++ b/MatterControlLib/ApplicationView/Config/BedConfig.cs @@ -162,14 +162,9 @@ namespace MatterHackers.MatterControl { await ApplicationController.Instance.Tasks.Execute("Loading G-Code".Localize(), Printer, (reporter, cancellationTokenSource) => { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); - this.LoadGCode(stream, cancellationTokenSource.Token, (progress0To1, status) => { - progressStatus.Status = status; - progressStatus.Progress0To1 = progress0To1; - reporter.Report(progressStatus); + reporter?.Invoke(progress0To1, status); }); this.Scene.Children.Modify(children => children.Clear()); @@ -676,7 +671,7 @@ namespace MatterHackers.MatterControl /// Allows for progress reporting /// Allows for cancellation during processing /// A task representing success - public async Task SaveChanges(IProgress progress, CancellationTokenSource cancellationTokenSource) + public async Task SaveChanges(Action progress, CancellationTokenSource cancellationTokenSource) { if (this.EditContext.ContentStore == null) { @@ -693,13 +688,10 @@ namespace MatterHackers.MatterControl return; } + + var status = "Saving Changes".Localize(); - var progressStatus = new ProgressStatus() - { - Status = "Saving Changes" - }; - - progress?.Report(progressStatus); + progress?.Invoke(0, status); if (this.Scene.Persistable) { @@ -721,12 +713,7 @@ namespace MatterHackers.MatterControl await this.Scene.PersistAssets((progress0to1, status) => { - if (progress != null) - { - progressStatus.Status = status; - progressStatus.Progress0To1 = progress0to1; - progress.Report(progressStatus); - } + progress?.Invoke(progress0to1, status); }); await this.EditContext?.Save(this.Scene); diff --git a/MatterControlLib/ApplicationView/Config/PrinterConfig.cs b/MatterControlLib/ApplicationView/Config/PrinterConfig.cs index 3e6be165c..ecb220b0c 100644 --- a/MatterControlLib/ApplicationView/Config/PrinterConfig.cs +++ b/MatterControlLib/ApplicationView/Config/PrinterConfig.cs @@ -338,7 +338,6 @@ namespace MatterHackers.MatterControl { waitingForHeat = HeatingStatus.Bed; - var progressStatus = new ProgressStatus(); heatStart = printerConnection.ActualBedTemperature; heatDistance = Math.Abs(printerConnection.TargetBedTemperature - heatStart); @@ -346,9 +345,8 @@ namespace MatterHackers.MatterControl && waitingForHeat == HeatingStatus.Bed) { var remainingDistance = Math.Abs(printerConnection.TargetBedTemperature - printerConnection.ActualBedTemperature); - progressStatus.Status = $"Heating Bed ({printerConnection.ActualBedTemperature:0}/{printerConnection.TargetBedTemperature:0})"; - progressStatus.Progress0To1 = (heatDistance - remainingDistance) / heatDistance; - reporter.Report(progressStatus); + var status = $"Heating Bed ({0:0}/{1:0})".Localize().FormatWith(printerConnection.ActualBedTemperature, printerConnection.TargetBedTemperature); + reporter?.Invoke((heatDistance - remainingDistance) / heatDistance, status); Thread.Sleep(10); } @@ -368,8 +366,6 @@ namespace MatterHackers.MatterControl { waitingForHeat = HeatingStatus.T0; - var progressStatus = new ProgressStatus(); - heatStart = printerConnection.GetActualHotendTemperature(0); heatDistance = Math.Abs(printerConnection.GetTargetHotendTemperature(0) - heatStart); @@ -377,9 +373,9 @@ namespace MatterHackers.MatterControl && waitingForHeat == HeatingStatus.T0) { var currentDistance = Math.Abs(printerConnection.GetTargetHotendTemperature(0) - printerConnection.GetActualHotendTemperature(0)); - progressStatus.Progress0To1 = (heatDistance - currentDistance) / heatDistance; - progressStatus.Status = $"Heating Nozzle ({printerConnection.GetActualHotendTemperature(0):0}/{printerConnection.GetTargetHotendTemperature(0):0})"; - reporter.Report(progressStatus); + var progress0To1 = (heatDistance - currentDistance) / heatDistance; + var status = $"Heating Nozzle ({0}/{1})".Localize().FormatWith(printerConnection.GetActualHotendTemperature(0), printerConnection.GetTargetHotendTemperature(0)); + reporter?.Invoke(progress0To1, status); Thread.Sleep(1000); } @@ -399,8 +395,6 @@ namespace MatterHackers.MatterControl { waitingForHeat = HeatingStatus.T1; - var progressStatus = new ProgressStatus(); - heatStart = printerConnection.GetActualHotendTemperature(1); heatDistance = Math.Abs(printerConnection.GetTargetHotendTemperature(1) - heatStart); @@ -408,9 +402,9 @@ namespace MatterHackers.MatterControl && waitingForHeat == HeatingStatus.T1) { var currentDistance = Math.Abs(printerConnection.GetTargetHotendTemperature(1) - printerConnection.GetActualHotendTemperature(1)); - progressStatus.Progress0To1 = (heatDistance - currentDistance) / heatDistance; - progressStatus.Status = $"Heating Nozzle ({printerConnection.GetActualHotendTemperature(1):0}/{printerConnection.GetTargetHotendTemperature(1):0})"; - reporter.Report(progressStatus); + var progress0To1 = (heatDistance - currentDistance) / heatDistance; + var status = $"Heating Nozzle ({printerConnection.GetActualHotendTemperature(1):0}/{printerConnection.GetTargetHotendTemperature(1):0})"; + reporter?.Invoke(progress0To1, status); Thread.Sleep(1000); } diff --git a/MatterControlLib/ApplicationView/Config/RunningTasksConfig.cs b/MatterControlLib/ApplicationView/Config/RunningTasksConfig.cs index fe20129c3..82e1d1e87 100644 --- a/MatterControlLib/ApplicationView/Config/RunningTasksConfig.cs +++ b/MatterControlLib/ApplicationView/Config/RunningTasksConfig.cs @@ -44,49 +44,52 @@ using System.Threading.Tasks; namespace MatterHackers.MatterControl { public class RunningTasksConfig - { - public event EventHandler TasksChanged; + { + public event EventHandler TasksChanged; - private ObservableCollection executingTasks = new ObservableCollection(); + private ObservableCollection executingTasks = new ObservableCollection(); - public IEnumerable RunningTasks => executingTasks.ToList(); + public IEnumerable RunningTasks => executingTasks.ToList(); - public RunningTasksConfig() - { - executingTasks.CollectionChanged += (s, e) => - { - UiThread.RunOnIdle(() => this.TasksChanged?.Invoke(this, null)); - }; - } + public RunningTasksConfig() + { + executingTasks.CollectionChanged += (s, e) => + { + UiThread.RunOnIdle(() => this.TasksChanged?.Invoke(this, null)); + }; + } - public Task Execute(string taskTitle, object owner, Func, CancellationTokenSource, Task> func, RunningTaskOptions taskActions = null) - { - var tokenSource = new CancellationTokenSource(); + public Task Execute(string taskTitle, object owner, Func, CancellationTokenSource, Task> func, RunningTaskOptions taskActions = null) + { + var tokenSource = new CancellationTokenSource(); - var taskDetails = new RunningTaskDetails(tokenSource) - { - Options = taskActions, - Title = taskTitle, - Owner = owner, - }; + var taskDetails = new RunningTaskDetails(tokenSource) + { + Options = taskActions, + Title = taskTitle, + Owner = owner, + }; - executingTasks.Add(taskDetails); + executingTasks.Add(taskDetails); - return Task.Run(async () => - { - try - { - await func?.Invoke(taskDetails, tokenSource); - } - catch - { - } + return Task.Run(async () => + { + try + { + await func?.Invoke((ratio, message) => + { + taskDetails.Report(ratio, message); + }, tokenSource); + } + catch + { + } - UiThread.RunOnIdle(() => - { - executingTasks.Remove(taskDetails); - }); - }); - } - } + UiThread.RunOnIdle(() => + { + executingTasks.Remove(taskDetails); + }); + }); + } + } } diff --git a/MatterControlLib/ApplicationView/ISceneContext.cs b/MatterControlLib/ApplicationView/ISceneContext.cs index 40df81426..d4693b175 100644 --- a/MatterControlLib/ApplicationView/ISceneContext.cs +++ b/MatterControlLib/ApplicationView/ISceneContext.cs @@ -82,7 +82,7 @@ namespace MatterHackers.MatterControl Task LoadLibraryContent(ILibraryItem libraryItem, Action progressReporter); - Task SaveChanges(IProgress progress, CancellationTokenSource cancellationToken); + Task SaveChanges(Action progress, CancellationTokenSource cancellationToken); bool HadSaveError { get; } diff --git a/MatterControlLib/ApplicationView/RunningTasks/RunningTaskDetails.cs b/MatterControlLib/ApplicationView/RunningTasks/RunningTaskDetails.cs index 881b81cce..38ed1685f 100644 --- a/MatterControlLib/ApplicationView/RunningTasks/RunningTaskDetails.cs +++ b/MatterControlLib/ApplicationView/RunningTasks/RunningTaskDetails.cs @@ -85,11 +85,11 @@ using Newtonsoft.Json.Linq; namespace MatterHackers.MatterControl { - public class RunningTaskDetails : IProgress + public class RunningTaskDetails { - public event EventHandler ProgressChanged; + public event EventHandler<(double ratio, string message)> ProgressChanged; - public Func DetailsItemAction { get; set; } + public Func DetailsItemAction { get; set; } private CancellationTokenSource tokenSource; @@ -139,12 +139,12 @@ namespace MatterHackers.MatterControl } } - public void Report(ProgressStatus progressStatus) + public void Report(double ratio, string message) { - this.ProgressChanged?.Invoke(this, progressStatus); - } + this.ProgressChanged?.Invoke(this, (ratio, message)); + } - public void CancelTask() + public void CancelTask() { this.tokenSource.Cancel(); } diff --git a/MatterControlLib/ApplicationView/SceneOperations.cs b/MatterControlLib/ApplicationView/SceneOperations.cs index c7812b78a..859a341bb 100644 --- a/MatterControlLib/ApplicationView/SceneOperations.cs +++ b/MatterControlLib/ApplicationView/SceneOperations.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ using MatterHackers.ImageProcessing; using MatterHackers.Localizations; using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.MatterControl.DesignTools.Primitives; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.PartPreviewWindow.View3D; using MatterHackers.MatterControl.SettingsManagement; @@ -1171,7 +1172,7 @@ namespace MatterHackers.MatterControl { try { - var updateItems = SheetObject3D.SortAndLockUpdateItems(selectedItem.Parent, (item) => + var updateItems = Expressions.SortAndLockUpdateItems(selectedItem.Parent, (item) => { if (item == selectedItem || item.Parent == selectedItem) { @@ -1181,7 +1182,7 @@ namespace MatterHackers.MatterControl return true; }, false); - SheetObject3D.SendInvalidateInRebuildOrder(updateItems, InvalidateType.Properties, null); + Expressions.SendInvalidateInRebuildOrder(updateItems, InvalidateType.Properties, null); } catch { @@ -1533,4 +1534,10 @@ namespace MatterHackers.MatterControl }; } } + + public interface IPrimaryOperationsSpecifier + { + IEnumerable GetOperations(); + } + } \ No newline at end of file diff --git a/MatterControlLib/CustomWidgets/ColorPicker/RadialColorPicker.cs b/MatterControlLib/CustomWidgets/ColorPicker/RadialColorPicker.cs index 239240534..46843b2da 100644 --- a/MatterControlLib/CustomWidgets/ColorPicker/RadialColorPicker.cs +++ b/MatterControlLib/CustomWidgets/ColorPicker/RadialColorPicker.cs @@ -38,175 +38,175 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { - public class RadialColorPicker : GuiWidget - { - private (double h, double s, double l) hsl; - private float alpha; - private Color downColor; + public class RadialColorPicker : GuiWidget + { + private (double h, double s, double l) hsl; + private float alpha; + private Color downColor; - private enum DownState - { - None, - OnRing, - OnTriangle, - } + private enum DownState + { + None, + OnRing, + OnTriangle, + } - private DownState downState = DownState.None; + private DownState downState = DownState.None; - public RadialColorPicker() - { - BackgroundColor = Color.White; + public RadialColorPicker() + { + BackgroundColor = Color.White; - this.Width = 100; - this.Height = 100; - } + this.Width = 100; + this.Height = 100; + } - public event EventHandler IncrementalColorChanged; + public event EventHandler IncrementalColorChanged; - public event EventHandler SelectedColorChanged; + public event EventHandler SelectedColorChanged; - public double RingWidth { get => Width / 10; } + public double RingWidth { get => Width / 10; } - public void SetColorWithoutChangeEvent(Color color) - { - color.ToColorF().GetHSL(out double h, out double s, out double l); - // if the color is not white or black, set the angle (otherwise leave it where it is) - if ((color.red != color.green || color.green != color.blue || color.blue != 0) - && (color.red != color.green || color.green != color.blue || color.blue != 255)) - { - hsl.h = h; - } - hsl.s = s; - hsl.l = l; - alpha = color.Alpha0To1; + public void SetColorWithoutChangeEvent(Color color) + { + color.ToColorF().GetHSL(out double h, out double s, out double l); + // if the color is not white or black, set the angle (otherwise leave it where it is) + if ((color.red != color.green || color.green != color.blue || color.blue != 0) + && (color.red != color.green || color.green != color.blue || color.blue != 255)) + { + hsl.h = h; + } + hsl.s = s; + hsl.l = l; + alpha = color.Alpha0To1; - Invalidate(); - } + Invalidate(); + } - public override void OnKeyDown(KeyEventArgs keyEvent) - { - if (downState != DownState.None - && keyEvent.KeyCode == Keys.Escape) - { - downState = DownState.None; - SelectedColor = downColor; - } + public override void OnKeyDown(KeyEventArgs keyEvent) + { + if (downState != DownState.None + && keyEvent.KeyCode == Keys.Escape) + { + downState = DownState.None; + SelectedColor = downColor; + } - base.OnKeyDown(keyEvent); - } + base.OnKeyDown(keyEvent); + } - public Color SelectedColor - { - get - { - return ColorF.FromHSL(hsl.h, hsl.s, hsl.l, alpha).ToColor(); - } + public Color SelectedColor + { + get + { + return ColorF.FromHSL(hsl.h, hsl.s, hsl.l, alpha).ToColor(); + } - set - { - if (value != SelectedColor) - { - SetColorWithoutChangeEvent(value); + set + { + if (value != SelectedColor) + { + SetColorWithoutChangeEvent(value); - SelectedColorChanged?.Invoke(this, null); - } - } - } + SelectedColorChanged?.Invoke(this, null); + } + } + } - public Color SelectedHueColor - { - get - { - return ColorF.FromHSL(hsl.h, 1, .5).ToColor(); - } + public Color SelectedHueColor + { + get + { + return ColorF.FromHSL(hsl.h, 1, .5).ToColor(); + } - set - { - value.ToColorF().GetHSL(out double h, out _, out _); - hsl.h = h; - } - } + set + { + value.ToColorF().GetHSL(out double h, out _, out _); + hsl.h = h; + } + } - private double InnerRadius - { - get - { - return RingRadius - RingWidth / 2; - } - } + private double InnerRadius + { + get + { + return RingRadius - RingWidth / 2; + } + } - private double RingRadius - { - get - { - return Width / 2 - RingWidth / 2 - 2; - } - } + private double RingRadius + { + get + { + return Width / 2 - RingWidth / 2 - 2; + } + } - public override void OnDraw(Graphics2D graphics2D) - { - var center = new Vector2(Width / 2, Height / 2); + public override void OnDraw(Graphics2D graphics2D) + { + var center = new Vector2(Width / 2, Height / 2); - // draw the big outside ring (color part) - DrawColorRing(graphics2D, RingRadius, RingWidth); + // draw the big outside ring (color part) + DrawColorRing(graphics2D, RingRadius, RingWidth); - // draw the inner triangle (color part) - DrawColorTriangle(graphics2D, InnerRadius, SelectedHueColor); + // draw the inner triangle (color part) + DrawColorTriangle(graphics2D, InnerRadius, SelectedHueColor); - // draw the big ring outline - graphics2D.Ring(center, RingRadius + RingWidth / 2, 1, Color.Black); - graphics2D.Ring(center, RingRadius - RingWidth / 2, 1, Color.Black); + // draw the big ring outline + graphics2D.Ring(center, RingRadius + RingWidth / 2, 1, Color.Black); + graphics2D.Ring(center, RingRadius - RingWidth / 2, 1, Color.Black); - // draw the triangle outline - var colorAngle = hsl.h * MathHelper.Tau; - graphics2D.Line(GetTrianglePoint(0, InnerRadius, colorAngle), GetTrianglePoint(1, InnerRadius, colorAngle), Color.Black); - graphics2D.Line(GetTrianglePoint(1, InnerRadius, colorAngle), GetTrianglePoint(2, InnerRadius, colorAngle), Color.Black); - graphics2D.Line(GetTrianglePoint(2, InnerRadius, colorAngle), GetTrianglePoint(0, InnerRadius, colorAngle), Color.Black); + // draw the triangle outline + var colorAngle = hsl.h * MathHelper.Tau; + graphics2D.Line(GetTrianglePoint(0, InnerRadius, colorAngle), GetTrianglePoint(1, InnerRadius, colorAngle), Color.Black); + graphics2D.Line(GetTrianglePoint(1, InnerRadius, colorAngle), GetTrianglePoint(2, InnerRadius, colorAngle), Color.Black); + graphics2D.Line(GetTrianglePoint(2, InnerRadius, colorAngle), GetTrianglePoint(0, InnerRadius, colorAngle), Color.Black); - // draw the color circle on the triangle - var unitPosition = new Vector2(hsl.s, hsl.l); - unitPosition.Y = agg_basics.Clamp(unitPosition.Y, .5 - (1 - unitPosition.X) / 2, .5 + (1 - unitPosition.X) / 2); + // draw the color circle on the triangle + var unitPosition = new Vector2(hsl.s, hsl.l); + unitPosition.Y = Util.Clamp(unitPosition.Y, .5 - (1 - unitPosition.X) / 2, .5 + (1 - unitPosition.X) / 2); - var triangleColorCenter = TriangleToWidgetTransform().Transform(unitPosition); - graphics2D.Circle(triangleColorCenter, RingWidth / 2 - 2, new Color(SelectedColor, 255)); - graphics2D.Ring(triangleColorCenter, RingWidth / 2 - 2, 2, Color.White); + var triangleColorCenter = TriangleToWidgetTransform().Transform(unitPosition); + graphics2D.Circle(triangleColorCenter, RingWidth / 2 - 2, new Color(SelectedColor, 255)); + graphics2D.Ring(triangleColorCenter, RingWidth / 2 - 2, 2, Color.White); - // draw the color circle on the ring - var ringColorCenter = center + Vector2.Rotate(new Vector2(RingRadius, 0), colorAngle); - graphics2D.Circle(ringColorCenter, - RingWidth / 2 - 2, - SelectedHueColor); - graphics2D.Ring(ringColorCenter, - RingWidth / 2 - 2, - 2, - Color.White); + // draw the color circle on the ring + var ringColorCenter = center + Vector2.Rotate(new Vector2(RingRadius, 0), colorAngle); + graphics2D.Circle(ringColorCenter, + RingWidth / 2 - 2, + SelectedHueColor); + graphics2D.Ring(ringColorCenter, + RingWidth / 2 - 2, + 2, + Color.White); - base.OnDraw(graphics2D); - } + base.OnDraw(graphics2D); + } - public override void OnMouseDown(MouseEventArgs mouseEvent) - { - downColor = SelectedColor; + public override void OnMouseDown(MouseEventArgs mouseEvent) + { + downColor = SelectedColor; - var center = new Vector2(Width / 2, Height / 2); - var direction = mouseEvent.Position - center; - var startColor = SelectedColor; + var center = new Vector2(Width / 2, Height / 2); + var direction = mouseEvent.Position - center; + var startColor = SelectedColor; - if (mouseEvent.Button == MouseButtons.Left) - { - if (direction.Length > RingRadius - RingWidth / 2 - && direction.Length < RingRadius + RingWidth / 2) - { - downState = DownState.OnRing; + if (mouseEvent.Button == MouseButtons.Left) + { + if (direction.Length > RingRadius - RingWidth / 2 + && direction.Length < RingRadius + RingWidth / 2) + { + downState = DownState.OnRing; - var colorAngle = Math.Atan2(direction.Y, direction.X); - if (colorAngle < 0) - { - colorAngle += MathHelper.Tau; - } + var colorAngle = Math.Atan2(direction.Y, direction.X); + if (colorAngle < 0) + { + colorAngle += MathHelper.Tau; + } - hsl.h = colorAngle / MathHelper.Tau; + hsl.h = colorAngle / MathHelper.Tau; if (hsl.s == 0) { @@ -215,34 +215,34 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker } Invalidate(); - } - else - { - var (inside, position) = WidgetToUnitTriangle(mouseEvent.Position); + } + else + { + var (inside, position) = WidgetToUnitTriangle(mouseEvent.Position); - if (inside) - { - downState = DownState.OnTriangle; - SetSLFromUnitPosition(position); - } + if (inside) + { + downState = DownState.OnTriangle; + SetSLFromUnitPosition(position); + } - Invalidate(); - } - } + Invalidate(); + } + } - if (startColor != SelectedColor) - { - IncrementalColorChanged?.Invoke(this, null); - } + if (startColor != SelectedColor) + { + IncrementalColorChanged?.Invoke(this, null); + } - base.OnMouseDown(mouseEvent); - } + base.OnMouseDown(mouseEvent); + } - private void SetSLFromUnitPosition(Vector2 position) - { - hsl.s = position.X; - hsl.l = position.Y; - /* + private void SetSLFromUnitPosition(Vector2 position) + { + hsl.s = position.X; + hsl.l = position.Y; + /* if (hsl.l > hsl.s) { @@ -251,183 +251,183 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker agg_basics.Clamp(unitPosition.Y, .5 - (1 - unitPosition.X) / 2, .5 + (1 - unitPosition.X) / 2); unitTrianglePosition = position; */ - } + } - public override void OnMouseMove(MouseEventArgs mouseEvent) - { - var startColor = SelectedColor; + public override void OnMouseMove(MouseEventArgs mouseEvent) + { + var startColor = SelectedColor; - switch (downState) - { - case DownState.OnRing: - var center = new Vector2(Width / 2, Height / 2); + switch (downState) + { + case DownState.OnRing: + var center = new Vector2(Width / 2, Height / 2); - var direction = mouseEvent.Position - center; - var colorAngle = Math.Atan2(direction.Y, direction.X); - if (colorAngle < 0) - { - colorAngle += MathHelper.Tau; - } - hsl.h = colorAngle / MathHelper.Tau; + var direction = mouseEvent.Position - center; + var colorAngle = Math.Atan2(direction.Y, direction.X); + if (colorAngle < 0) + { + colorAngle += MathHelper.Tau; + } + hsl.h = colorAngle / MathHelper.Tau; - Invalidate(); - break; + Invalidate(); + break; - case DownState.OnTriangle: - SetSLFromUnitPosition(WidgetToUnitTriangle(mouseEvent.Position).position); - Invalidate(); - break; - } + case DownState.OnTriangle: + SetSLFromUnitPosition(WidgetToUnitTriangle(mouseEvent.Position).position); + Invalidate(); + break; + } - if (startColor != SelectedColor) - { - IncrementalColorChanged?.Invoke(this, null); - } + if (startColor != SelectedColor) + { + IncrementalColorChanged?.Invoke(this, null); + } - base.OnMouseMove(mouseEvent); - } + base.OnMouseMove(mouseEvent); + } - public override void OnMouseUp(MouseEventArgs mouseEvent) - { - downState = DownState.None; + public override void OnMouseUp(MouseEventArgs mouseEvent) + { + downState = DownState.None; - if (downColor != SelectedColor) - { - SelectedColorChanged?.Invoke(this, null); - } + if (downColor != SelectedColor) + { + SelectedColorChanged?.Invoke(this, null); + } - base.OnMouseUp(mouseEvent); - } + base.OnMouseUp(mouseEvent); + } - private void DrawColorRing(Graphics2D graphics2D, double radius, double width) - { - if (graphics2D is Graphics2DOpenGL graphicsGL) - { - graphicsGL.PushOrthoProjection(); + private void DrawColorRing(Graphics2D graphics2D, double radius, double width) + { + if (graphics2D is Graphics2DOpenGL graphicsGL) + { + graphicsGL.PushOrthoProjection(); - GL.Disable(EnableCap.Texture2D); - GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); - GL.Enable(EnableCap.Blend); + GL.Disable(EnableCap.Texture2D); + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.Enable(EnableCap.Blend); - var outer = radius + width / 2; - var inner = radius - width / 2; - GL.Begin(BeginMode.TriangleStrip); + var outer = radius + width / 2; + var inner = radius - width / 2; + GL.Begin(BeginMode.TriangleStrip); - for (int i = 0; i <= 360; i++) - { - var color = ColorF.FromHSL(i / 360.0, 1, .5); - var angle = MathHelper.DegreesToRadians(i); + for (int i = 0; i <= 360; i++) + { + var color = ColorF.FromHSL(i / 360.0, 1, .5); + var angle = MathHelper.DegreesToRadians(i); - GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); - GL.Vertex2(GetAtAngle(angle, outer, true)); - GL.Vertex2(GetAtAngle(angle, inner, true)); - } + GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); + GL.Vertex2(GetAtAngle(angle, outer, true)); + GL.Vertex2(GetAtAngle(angle, inner, true)); + } - GL.End(); + GL.End(); - graphicsGL.PopOrthoProjection(); - } - } + graphicsGL.PopOrthoProjection(); + } + } - private void DrawColorTriangle(Graphics2D graphics2D, double radius, Color color) - { - if (graphics2D is Graphics2DOpenGL graphicsGL) - { - graphicsGL.PushOrthoProjection(); + private void DrawColorTriangle(Graphics2D graphics2D, double radius, Color color) + { + if (graphics2D is Graphics2DOpenGL graphicsGL) + { + graphicsGL.PushOrthoProjection(); - GL.Disable(EnableCap.Texture2D); - GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); - GL.Enable(EnableCap.Blend); + GL.Disable(EnableCap.Texture2D); + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.Enable(EnableCap.Blend); - GL.Begin(BeginMode.Triangles); - GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); - var colorAngle = hsl.h * MathHelper.Tau; - GL.Vertex2(GetTrianglePoint(0, radius, colorAngle, true)); - GL.Color4(Color.White); - GL.Vertex2(GetTrianglePoint(1, radius, colorAngle, true)); - GL.Color4(Color.Black); - GL.Vertex2(GetTrianglePoint(2, radius, colorAngle, true)); + GL.Begin(BeginMode.Triangles); + GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); + var colorAngle = hsl.h * MathHelper.Tau; + GL.Vertex2(GetTrianglePoint(0, radius, colorAngle, true)); + GL.Color4(Color.White); + GL.Vertex2(GetTrianglePoint(1, radius, colorAngle, true)); + GL.Color4(Color.Black); + GL.Vertex2(GetTrianglePoint(2, radius, colorAngle, true)); - GL.End(); + GL.End(); - graphicsGL.PopOrthoProjection(); - } - } + graphicsGL.PopOrthoProjection(); + } + } - private Vector2 GetAtAngle(double angle, double radius, bool screenSpace) - { - var start = new Vector2(radius, 0); + private Vector2 GetAtAngle(double angle, double radius, bool screenSpace) + { + var start = new Vector2(radius, 0); - var position = default(Vector2); - if (screenSpace) - { - position = this.TransformToScreenSpace(this.Position); - } + var position = default(Vector2); + if (screenSpace) + { + position = this.TransformToScreenSpace(this.Position); + } - var center = new Vector2(Width / 2, Height / 2); - return position + center + Vector2.Rotate(start, angle); - } + var center = new Vector2(Width / 2, Height / 2); + return position + center + Vector2.Rotate(start, angle); + } - private Vector2 GetTrianglePoint(int index, double radius, double pontingAngle, bool screenSpace = false) - { - switch (index) - { - case 0: - return GetAtAngle(pontingAngle, radius, screenSpace); + private Vector2 GetTrianglePoint(int index, double radius, double pontingAngle, bool screenSpace = false) + { + switch (index) + { + case 0: + return GetAtAngle(pontingAngle, radius, screenSpace); - case 1: - return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(120), radius, screenSpace); + case 1: + return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(120), radius, screenSpace); - case 2: - return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(240), radius, screenSpace); - } + case 2: + return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(240), radius, screenSpace); + } - return Vector2.Zero; - } + return Vector2.Zero; + } - private Affine TriangleToWidgetTransform() - { - var center = new Vector2(Width / 2, Height / 2); - var leftSize = .5; - var sizeToTop = Math.Sin(MathHelper.DegreesToRadians(60)); + private Affine TriangleToWidgetTransform() + { + var center = new Vector2(Width / 2, Height / 2); + var leftSize = .5; + var sizeToTop = Math.Sin(MathHelper.DegreesToRadians(60)); - Affine total = Affine.NewIdentity(); - // scale to -1 to 1 coordinates - total *= Affine.NewScaling(1 + leftSize, sizeToTop * 2); - // center - total *= Affine.NewTranslation(-leftSize, -sizeToTop); - // rotate to correct color - var colorAngle = hsl.h * MathHelper.Tau; - total *= Affine.NewRotation(colorAngle); - // scale to radius - total *= Affine.NewScaling(InnerRadius); - // move to center - total *= Affine.NewTranslation(center); - return total; - } + Affine total = Affine.NewIdentity(); + // scale to -1 to 1 coordinates + total *= Affine.NewScaling(1 + leftSize, sizeToTop * 2); + // center + total *= Affine.NewTranslation(-leftSize, -sizeToTop); + // rotate to correct color + var colorAngle = hsl.h * MathHelper.Tau; + total *= Affine.NewRotation(colorAngle); + // scale to radius + total *= Affine.NewScaling(InnerRadius); + // move to center + total *= Affine.NewTranslation(center); + return total; + } - private (bool inside, Vector2 position) WidgetToUnitTriangle(Vector2 widgetPosition) - { - var trianglePosition = TriangleToWidgetTransform() - .InverseTransform(widgetPosition); + private (bool inside, Vector2 position) WidgetToUnitTriangle(Vector2 widgetPosition) + { + var trianglePosition = TriangleToWidgetTransform() + .InverseTransform(widgetPosition); - bool changed = ClampTrianglePosition(ref trianglePosition); + bool changed = ClampTrianglePosition(ref trianglePosition); - return (!changed, trianglePosition); - } + return (!changed, trianglePosition); + } - private static bool ClampTrianglePosition(ref Vector2 trianglePosition) - { - bool changed = false; - trianglePosition.X = agg_basics.Clamp(trianglePosition.X, 0, 1, ref changed); - trianglePosition.Y = agg_basics.Clamp(trianglePosition.Y, 0, 1, ref changed); + private static bool ClampTrianglePosition(ref Vector2 trianglePosition) + { + bool changed = false; + trianglePosition.X = Util.Clamp(trianglePosition.X, 0, 1, ref changed); + trianglePosition.Y = Util.Clamp(trianglePosition.Y, 0, 1, ref changed); - trianglePosition.Y = agg_basics.Clamp(trianglePosition.Y, - .5 - (1 - trianglePosition.X) / 2, - .5 + (1 - trianglePosition.X) / 2, - ref changed); + trianglePosition.Y = Util.Clamp(trianglePosition.Y, + .5 - (1 - trianglePosition.X) / 2, + .5 + (1 - trianglePosition.X) / 2, + ref changed); - return changed; - } - } + return changed; + } + } } \ No newline at end of file diff --git a/MatterControlLib/CustomWidgets/SceneOperation.cs b/MatterControlLib/CustomWidgets/SceneOperation.cs index d600f49bc..d8210ab8a 100644 --- a/MatterControlLib/CustomWidgets/SceneOperation.cs +++ b/MatterControlLib/CustomWidgets/SceneOperation.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2019, John Lewin +Copyright (c) 2023, John Lewin, Lars Brubaker All rights reserved. @@ -152,7 +152,7 @@ namespace MatterHackers.Agg.UI activeButtonID = InitialSelectionIndex; } - activeButtonID = agg_basics.Clamp(activeButtonID, 0, this.Operations.Count - 1); + activeButtonID = Util.Clamp(activeButtonID, 0, this.Operations.Count - 1); return this.Operations[activeButtonID]; } diff --git a/MatterControlLib/DesignTools/Constants.cs b/MatterControlLib/DesignTools/Constants.cs index 3ef466bdb..8d4eb8416 100644 --- a/MatterControlLib/DesignTools/Constants.cs +++ b/MatterControlLib/DesignTools/Constants.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -29,18 +29,18 @@ either expressed or implied, of the FreeBSD Project. namespace MatterHackers.MatterControl.DesignTools { - public static class Constants - { - /// - /// Gets the height of the geometry that shows the shape of a path object (it cannot be used in mesh operations). - /// - public static double PathPolygonsHeight => .2; + public static class Constants + { + /// + /// Gets the height of the geometry that shows the shape of a path object (it cannot be used in mesh operations). + /// + public static double PathPolygonsHeight => .2; - /// - /// Gets the alpha of the non z-buffered lines in the editor - /// - public static int LineAlpha => 80; + /// + /// Gets the alpha of the non z-buffered lines in the editor + /// + public static int LineAlpha => 80; - public static int Controls3DAlpha => 80; - } + public static int Controls3DAlpha => 80; + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/EditorTools/EditorToolsPlugin.cs b/MatterControlLib/DesignTools/EditorTools/EditorToolsPlugin.cs index 742737bde..20b437672 100644 --- a/MatterControlLib/DesignTools/EditorTools/EditorToolsPlugin.cs +++ b/MatterControlLib/DesignTools/EditorTools/EditorToolsPlugin.cs @@ -40,7 +40,7 @@ namespace MatterHackers.Plugins.EditorTools { var applicationController = ApplicationController.Instance; - var primitives = new PrimitivesContainer(); + var primitives = new Primitives3DContainer(); primitives.Load(); foreach (var item in primitives.Items.OfType()) diff --git a/MatterControlLib/DesignTools/Expressions.cs b/MatterControlLib/DesignTools/Expressions.cs new file mode 100644 index 000000000..a2ecaaf98 --- /dev/null +++ b/MatterControlLib/DesignTools/Expressions.cs @@ -0,0 +1,571 @@ +/* +Copyright (c) 2023, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.MatterControl.DesignTools.Operations; +using org.mariuszgromada.math.mxparser; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using static MatterHackers.MatterControl.DesignTools.SheetObject3D; + +namespace MatterHackers.MatterControl.DesignTools +{ + public static class Expressions + { + public const BindingFlags OwnedPropertiesOnly = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; + + private static readonly Regex ConstantFinder = new Regex("(?<=\\[).+?(?=\\])", RegexOptions.CultureInvariant | RegexOptions.Compiled); + + private static readonly Type[] ExpressionTypes = + { + typeof(StringOrExpression), + typeof(DoubleOrExpression), + typeof(IntOrExpression), + }; + + private static Dictionary> constants = new Dictionary>() + { + // length + ["cm"] = (owner) => 10, + ["m"] = (owner) => 1000, + ["inch"] = (owner) => 25.4, + ["ft"] = (owner) => 304.8, + // math constant + ["pi"] = (owner) => Math.PI, + ["tau"] = (owner) => Math.PI * 2, + ["e"] = (owner) => Math.E, + // functions + ["rand"] = (owner) => rand.NextDouble(), + // array function + ["index"] = (owner) => RetrieveArrayIndex(owner, 0), + ["index0"] = (owner) => RetrieveArrayIndex(owner, 0), + ["index1"] = (owner) => RetrieveArrayIndex(owner, 1), + ["index2"] = (owner) => RetrieveArrayIndex(owner, 2), + }; + + private static Random rand = new Random(); + + public static T CastResult(string value, string inputExpression) + { + if (typeof(T) == typeof(string)) + { + // if parsing the equation resulted in NaN as the output + if (value == "NaN") + { + // return the actual expression + return (T)(object)inputExpression; + } + + // get the value of the cell + return (T)(object)value; + } + + if (typeof(T) == typeof(double)) + { + if (double.TryParse(value, out double doubleValue) + && !double.IsNaN(doubleValue) + && !double.IsInfinity(doubleValue)) + { + return (T)(object)doubleValue; + } + // else return an error + return (T)(object).1; + } + + if (typeof(T) == typeof(int)) + { + if (double.TryParse(value, out double doubleValue) + && !double.IsNaN(doubleValue) + && !double.IsInfinity(doubleValue)) + { + return (T)(object)(int)Math.Round(doubleValue); + } + // else return an error + return (T)(object)1; + } + + return (T)(object)default(T); + } + + public static T EvaluateExpression(IObject3D owner, string inExpression) + { + var inputExpression = inExpression; + + inputExpression = SearchSiblingProperties(owner, inputExpression); + + inputExpression = ReplaceConstantsWithValues(owner, inputExpression); + + // check if the expression is an equation (starts with "=") + if (inputExpression.Length > 0 && inputExpression[0] == '=') + { + // look through all the parents + var variableContainer = FindFirstVariableContainer(owner); + if (variableContainer != null) + { + // try to manage the cell into the correct data type + string value = variableContainer.EvaluateExpression(inputExpression); + return CastResult(value, inputExpression); + } + + // could not find a sheet, try to evaluate the expression directly + var evaluator = new Expression(inputExpression.Substring(1).ToLower()); + if (evaluator.checkSyntax()) + { + Debug.WriteLine(evaluator.getErrorMessage()); + } + + return CastResult(evaluator.calculate().ToString(), inputExpression); + } + else // not an equation so try to parse it directly + { + if (typeof(T) == typeof(string)) + { + return (T)(object)inputExpression; + } + + double.TryParse(inputExpression, out var result); + + if (typeof(T) == typeof(double)) + { + return (T)(object)result; + } + if (typeof(T) == typeof(int)) + { + return (T)(object)(int)Math.Round(result); + } + + return (T)(object)0; + } + } + + public static IEnumerable GetActiveExpression(IObject3D item, string checkForString, bool startsWith) + { + foreach (var property in PublicPropertyEditor.GetEditablePropreties(item)) + { + var propertyValue = property.Value; + + if (propertyValue is DirectOrExpression directOrExpression) + { + if (startsWith) + { + if (directOrExpression.Expression.StartsWith(checkForString)) + { + // WIP: check if the value has actually changed, this will update every object on any cell change + yield return directOrExpression; + } + } + else + { + if (directOrExpression.Expression.Contains(checkForString)) + { + yield return directOrExpression; + } + } + } + } + } + + public static IEnumerable GetComponentExpressions(ComponentObject3D component, string checkForString, bool startsWith) + { + for (var i = 0; i < component.SurfacedEditors.Count; i++) + { + var (cellId, cellData) = component.DecodeContent(i); + + if (cellId != null) + { + if (startsWith) + { + if (cellData.StartsWith(checkForString)) + { + // WIP: check if the value has actually changed, this will update every object on any cell change + yield return i; + } + } + else + { + if (cellData.Contains(checkForString)) + { + yield return i; + } + } + } + } + } + + public static IEnumerable GetExpressionPropreties(IObject3D item) + { + return item.GetType().GetProperties(OwnedPropertiesOnly) + .Where(pi => ExpressionTypes.Contains(pi.PropertyType) + && pi.GetGetMethod() != null + && pi.GetSetMethod() != null) + .Select(p => new EditableProperty(p, item)); + } + + public static bool HasExpressionWithString(IObject3D itemToCheck, string checkForString, bool startsWith) + { + foreach (var item in itemToCheck.DescendantsAndSelf()) + { + if (GetActiveExpression(item, checkForString, startsWith).Any() + || itemToCheck is ComponentObject3D component + && GetComponentExpressions(component, checkForString, startsWith).Any()) + { + // three is one so return true + return true; + } + } + + return false; + } + + /// + /// Check if there are any references from the item to the sheet. + /// + /// The item to validate editable properties on + /// The sheet to check if this object references + /// + public static bool NeedRebuild(IObject3D itemToCheck, InvalidateArgs invalidateArgs) + { + if (!invalidateArgs.InvalidateType.HasFlag(InvalidateType.SheetUpdated)) + { + return false; + } + + if (invalidateArgs.Source is SheetObject3D sheet) + { + // Check if the sheet is the first sheet parent of this item (if not it will not change it's data). + if (FindFirstVariableContainer(itemToCheck) == sheet) + { + return HasExpressionWithString(itemToCheck, "=", true); + } + } + + return false; + } + + public static int RetrieveArrayIndex(IObject3D item, int level) + { + var arrayObject = FindParentArray(item, level); + + if (arrayObject != null) + { + int index = 0; + foreach (var child in arrayObject.Children) + { + if (!(child is OperationSourceObject3D)) + { + if (child.DescendantsAndSelf().Where(i => i == item).Any()) + { + return index; + } + + index++; + } + } + } + + return 0; + } + + public static RunningInterval SendInvalidateInRebuildOrder(List updateItems, + InvalidateType invalidateType, + IObject3D sender = null) + { + // and send the invalidate + RunningInterval runningInterval = null; + void RebuildWhenUnlocked() + { + var count = updateItems.Count; + if (count > 0) + { + // get the last item from the list + var lastIndex = count - 1; + var lastUpdateItem = updateItems[lastIndex]; + // we start with everything locked, so unlock the last layer and tell it to rebuild + if (lastUpdateItem.rebuildLock != null) + { + // release the lock and rebuild + // and ask it to update + var depthToBuild = lastUpdateItem.depth; + for (int i = 0; i < updateItems.Count; i++) + { + var updateItem = updateItems[i]; + if (updateItem.depth == lastUpdateItem.depth) + { + updateItem.rebuildLock.Dispose(); + updateItem.rebuildLock = null; + var updateSender = sender == null ? updateItem.item : sender; + updateItem.item.Invalidate(new InvalidateArgs(updateSender, invalidateType)); + } + } + } + else if (updateItems.Where(i => + { + return i.depth == lastUpdateItem.depth && i.item.RebuildLocked; + }).Any()) + { + // wait for the current rebuild to end (the one we requested above) + return; + } + else + { + // now that all the items at this level have rebuilt, remove them from out tracking + for (int i = updateItems.Count - 1; i >= 0; i--) + { + if (updateItems[i].depth == lastUpdateItem.depth) + { + updateItems.RemoveAt(i); + } + } + } + } + else + { + UiThread.ClearInterval(runningInterval); + } + } + + // rebuild depth first + runningInterval = UiThread.SetInterval(RebuildWhenUnlocked, .01); + + return runningInterval; + } + + public static List SortAndLockUpdateItems(IObject3D root, Func includeObject, bool checkForExpression) + { + var requiredUpdateItems = new Dictionary(); + foreach (var child in root.Descendants()) + { + if (includeObject(child)) + { + var parent = child; + var depthToThis = 0; + while (parent.Parent != root) + { + depthToThis++; + parent = parent.Parent; + } + + AddItemsRequiringUpdateToDictionary(child, requiredUpdateItems, depthToThis, includeObject, checkForExpression); + } + } + + var updateItems = requiredUpdateItems.Values.ToList(); + // sort them + updateItems.Sort((a, b) => a.depth.CompareTo(b.depth)); + + // lock everything + foreach (var depthItem in updateItems) + { + depthItem.rebuildLock = depthItem.item.RebuildLock(); + } + + return updateItems; + } + + //private static bool HasValuesThatWillChange(IObject3D item) + // { + // // enumerate public properties on child + // foreach (var property in GetExpressionPropreties(item)) + // { + // var propertyValue = property.Value; + + // if (propertyValue is IDirectOrExpression expression) + // { + // // return the value + // var currentValue = item.GetType().GetProperty(property.Name).GetValue(child, null).ToString(); + // var newValue = EvaluateExpression(item, propertyValue.ToString()).ToString(); + // inExpression = inExpression.Replace("[" + constant + "]", value); + // } + // } + //} + + private static void AddItemsRequiringUpdateToDictionary(IObject3D inItem, + Dictionary updatedItems, + int inDepth, + Func includeObject, + bool checkForExpression) + { + // process depth first + foreach (var child in inItem.Children) + { + AddItemsRequiringUpdateToDictionary(child, updatedItems, inDepth + 1, includeObject, checkForExpression); + } + + var depth2 = inDepth; + if (includeObject(inItem) + && (!checkForExpression || HasExpressionWithString(inItem, "=", true))) + { + var itemToAdd = inItem; + while (itemToAdd != null + && depth2 >= 0) + { + updatedItems[itemToAdd] = new UpdateItem() + { + depth = depth2, + item = itemToAdd + }; + depth2--; + itemToAdd = itemToAdd?.Parent; + } + } + } + + /// + /// Find the sheet that the given item will reference + /// + /// The item to start the search from + /// + private static IVariableContainer FindFirstVariableContainer(IObject3D item) + { + // look through all the parents + foreach (var parent in item.Parents()) + { + // then each child of any give parent + foreach (var sibling in parent.Children) + { + // if it is a sheet + if (sibling != item + && sibling is IVariableContainer variableContainer) + { + return variableContainer; + } + } + } + + return null; + } + + private static ArrayObject3D FindParentArray(IObject3D item, int wantLevel) + { + int foundLevel = 0; + // look through all the parents + foreach (var parent in item.Parents()) + { + // if it is a ArrayObject3D + if (parent is ArrayObject3D arrayObject) + { + if (foundLevel == wantLevel) + { + return arrayObject; + } + + foundLevel++; + } + } + + return null; + } + + private static string GetDisplayName(PropertyInfo prop) + { + var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); + return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase(); + } + + private static string ReplaceConstantsWithValues(IObject3D owner, string stringWithConstants) + { + string Replace(string inputString, string setting) + { + if (constants.ContainsKey(setting)) + { + var value = constants[setting]; + + // braces then brackets replacement + inputString = inputString.Replace("[" + setting + "]", value(owner).ToString()); + } + + return inputString; + } + + MatchCollection matches = ConstantFinder.Matches(stringWithConstants); + + for (int i = 0; i < matches.Count; i++) + { + var replacementTerm = matches[i].Value; + stringWithConstants = Replace(stringWithConstants, replacementTerm); + } + + return stringWithConstants; + } + + private static string SearchSiblingProperties(IObject3D owner, string inExpression) + { + var parent = owner.Parent; + if (parent != null) + { + var matches = ConstantFinder.Matches(inExpression); + + for (int i = 0; i < matches.Count; i++) + { + var constant = matches[i].Value; + // split inExpression on . + var splitExpression = constant.Split('.'); + if (splitExpression.Length == 2) + { + foreach (var child in parent.Children) + { + // skip if owner + if (child != owner) + { + var itemName = splitExpression[0]; + var propertyName = splitExpression[1]; + // if child has the same name as itemName + if (child.Name == itemName) + { + // enumerate public properties on child + foreach (var property in child.GetType().GetProperties()) + { + var displayName = GetDisplayName(property); + // if property name matches propertyName + if (displayName == propertyName) + { + // return the value + var expression = child.GetType().GetProperty(property.Name).GetValue(child, null).ToString(); + var value = EvaluateExpression(child, expression).ToString(); + inExpression = inExpression.Replace("[" + constant + "]", value); + } + } + } + } + } + } + } + } + + return inExpression; + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Lithophane.cs b/MatterControlLib/DesignTools/Lithophane.cs index 4159cefc5..68a78f68a 100644 --- a/MatterControlLib/DesignTools/Lithophane.cs +++ b/MatterControlLib/DesignTools/Lithophane.cs @@ -1,199 +1,215 @@ /* -Copyright (c) 2018, John Lewin - */ +Copyright (c) 2023, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ -using System; -using System.Diagnostics; -using System.Linq; -using MatterHackers.Agg; using MatterHackers.Agg.Image; using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; +using System; +using System.Diagnostics; +using System.Linq; namespace MatterHackers.MatterControl.Plugins.Lithophane { - public static class Lithophane - { - class PixelInfo - { - public Vector3 Top { get; set; } - public Vector3 Bottom { get; set; } - } + public static class Lithophane + { + public interface IImageData + { + int Height { get; } + byte[] Pixels { get; } - public static Mesh Generate(IImageData resizedImage, double maxZ, double nozzleWidth, double pixelsPerMM, bool invert, IProgress reporter) - { - // TODO: Move this to a user supplied value - double baseThickness = nozzleWidth; // base thickness (in mm) - double zRange = maxZ - baseThickness; + int Width { get; } + } - // Dimensions of image - var width = resizedImage.Width; - var height = resizedImage.Height; + public static Mesh Generate(IImageData resizedImage, double maxZ, double nozzleWidth, double pixelsPerMM, bool invert, Action reporter) + { + // TODO: Move this to a user supplied value + double baseThickness = nozzleWidth; // base thickness (in mm) + double zRange = maxZ - baseThickness; - var zScale = zRange / 255; + // Dimensions of image + var width = resizedImage.Width; + var height = resizedImage.Height; - var pixelData = resizedImage.Pixels; + var zScale = zRange / 255; - Stopwatch stopwatch = Stopwatch.StartNew(); + var pixelData = resizedImage.Pixels; - var mesh = new Mesh(); + Stopwatch stopwatch = Stopwatch.StartNew(); - //var rescale = (double)onPlateWidth / imageData.Width; - var rescale = 1; + var mesh = new Mesh(); - var progressStatus = new ProgressStatus(); + //var rescale = (double)onPlateWidth / imageData.Width; + var rescale = 1; + // Build an array of PixelInfo objects from each pixel + // Collapse from 4 bytes per pixel to one - makes subsequent processing more logical and has minimal cost + var pixels = pixelData.Where((x, i) => i % 4 == 0) + // Interpolate the pixel color to zheight + .Select(b => baseThickness + (invert ? 255 - b : b) * zScale) - // Build an array of PixelInfo objects from each pixel - // Collapse from 4 bytes per pixel to one - makes subsequent processing more logical and has minimal cost - var pixels = pixelData.Where((x, i) => i % 4 == 0) + // Project to Vector3 for each pixel at the computed x/y/z + .Select((z, i) => new Vector3( + i % width * rescale, + (i - i % width) / width * rescale * -1, + z)) + // Project to PixelInfo, creating a mirrored Vector3 at z0, paired together and added to the mesh + .Select(vec => + { + var pixelInfo = new PixelInfo() + { + Top = vec, + Bottom = new Vector3(vec.X, vec.Y, 0) + }; - // Interpolate the pixel color to zheight - .Select(b => baseThickness + (invert ? 255 - b : b) * zScale) + mesh.Vertices.Add(pixelInfo.Top); + mesh.Vertices.Add(pixelInfo.Bottom); - // Project to Vector3 for each pixel at the computed x/y/z - .Select((z, i) => new Vector3( - i % width * rescale, - (i - i % width) / width * rescale * -1, - z)) - // Project to PixelInfo, creating a mirrored Vector3 at z0, paired together and added to the mesh - .Select(vec => - { - var pixelInfo = new PixelInfo() - { - Top = vec, - Bottom = new Vector3(vec.X, vec.Y, 0) - }; + return pixelInfo; + }).ToArray(); - mesh.Vertices.Add(pixelInfo.Top); - mesh.Vertices.Add(pixelInfo.Bottom); + Console.WriteLine("ElapsedTime - PixelInfo Linq Generation: {0}", stopwatch.ElapsedMilliseconds); + stopwatch.Restart(); - return pixelInfo; - }).ToArray(); + // Select pixels along image edges + var backRow = pixels.Take(width).Reverse().ToArray(); + var frontRow = pixels.Skip((height - 1) * width).Take(width).ToArray(); + var leftRow = pixels.Where((x, i) => i % width == 0).ToArray(); + var rightRow = pixels.Where((x, i) => (i + 1) % width == 0).Reverse().ToArray(); - Console.WriteLine("ElapsedTime - PixelInfo Linq Generation: {0}", stopwatch.ElapsedMilliseconds); - stopwatch.Restart(); + int k, + nextJ, + nextK; - // Select pixels along image edges - var backRow = pixels.Take(width).Reverse().ToArray(); - var frontRow = pixels.Skip((height - 1) * width).Take(width).ToArray(); - var leftRow = pixels.Where((x, i) => i % width == 0).ToArray(); - var rightRow = pixels.Where((x, i) => (i + 1) % width == 0).Reverse().ToArray(); + var notificationInterval = 100; - int k, - nextJ, - nextK; + var workCount = (resizedImage.Width - 1) * (resizedImage.Height - 1) + + (height - 1) + + (width - 1); - var notificationInterval = 100; + double workIndex = 0; - var workCount = (resizedImage.Width - 1) * (resizedImage.Height - 1) + - (height - 1) + - (width - 1); + // Vertical faces: process each row and column, creating the top and bottom faces as appropriate + for (int i = 0; i < resizedImage.Height - 1; ++i) + { + var startAt = i * width; - double workIndex = 0; + // Process each column + for (int j = startAt; j < startAt + resizedImage.Width - 1; ++j) + { + k = j + 1; + nextJ = j + resizedImage.Width; + nextK = nextJ + 1; - // Vertical faces: process each row and column, creating the top and bottom faces as appropriate - for (int i = 0; i < resizedImage.Height - 1; ++i) - { - var startAt = i * width; + // Create north, then south face + mesh.CreateFace(new[] { pixels[k].Top, pixels[j].Top, pixels[nextJ].Top, pixels[nextK].Top }); + mesh.CreateFace(new[] { pixels[j].Bottom, pixels[k].Bottom, pixels[nextK].Bottom, pixels[nextJ].Bottom }); + workIndex++; - // Process each column - for (int j = startAt; j < startAt + resizedImage.Width - 1; ++j) - { - k = j + 1; - nextJ = j + resizedImage.Width; - nextK = nextJ + 1; + if (workIndex % notificationInterval == 0) + { + reporter?.Invoke(workIndex / workCount, null); + } + } + } - // Create north, then south face - mesh.CreateFace(new [] { pixels[k].Top, pixels[j].Top, pixels[nextJ].Top, pixels[nextK].Top }); - mesh.CreateFace(new [] { pixels[j].Bottom, pixels[k].Bottom, pixels[nextK].Bottom, pixels[nextJ].Bottom }); - workIndex++; + // Side faces: East/West + for (int j = 0; j < height - 1; ++j) + { + //Next row + k = j + 1; - if (workIndex % notificationInterval == 0) - { - progressStatus.Progress0To1 = workIndex / workCount; - reporter.Report(progressStatus); - } - } - } + // Create east, then west face + mesh.CreateFace(new[] { leftRow[k].Top, leftRow[j].Top, leftRow[j].Bottom, leftRow[k].Bottom }); + mesh.CreateFace(new[] { rightRow[k].Top, rightRow[j].Top, rightRow[j].Bottom, rightRow[k].Bottom }); + workIndex++; - // Side faces: East/West - for (int j = 0; j < height - 1; ++j) - { - //Next row - k = j + 1; + if (workIndex % notificationInterval == 0) + { + reporter?.Invoke(workIndex / workCount, null); + } + } - // Create east, then west face - mesh.CreateFace(new [] { leftRow[k].Top, leftRow[j].Top, leftRow[j].Bottom, leftRow[k].Bottom }); - mesh.CreateFace(new [] { rightRow[k].Top, rightRow[j].Top, rightRow[j].Bottom, rightRow[k].Bottom }); - workIndex++; + // Side faces: North/South + for (int j = 0; j < width - 1; ++j) + { + // Next row + k = j + 1; - if (workIndex % notificationInterval == 0) - { - progressStatus.Progress0To1 = workIndex / workCount; - reporter.Report(progressStatus); - } - } + // Create north, then south face + mesh.CreateFace(new[] { frontRow[k].Top, frontRow[j].Top, frontRow[j].Bottom, frontRow[k].Bottom }); + mesh.CreateFace(new[] { backRow[k].Top, backRow[j].Top, backRow[j].Bottom, backRow[k].Bottom }); + workIndex++; - // Side faces: North/South - for (int j = 0; j < width - 1; ++j) - { - // Next row - k = j + 1; + if (workIndex % notificationInterval == 0) + { + reporter?.Invoke(workIndex / workCount, null); + } + } - // Create north, then south face - mesh.CreateFace(new [] { frontRow[k].Top, frontRow[j].Top, frontRow[j].Bottom, frontRow[k].Bottom }); - mesh.CreateFace(new [] { backRow[k].Top, backRow[j].Top, backRow[j].Bottom, backRow[k].Bottom }); - workIndex++; + Console.WriteLine("ElapsedTime - Face Generation: {0}", stopwatch.ElapsedMilliseconds); - if (workIndex % notificationInterval == 0) - { - progressStatus.Progress0To1 = workIndex / workCount; - reporter.Report(progressStatus); - } - } + return mesh; + } - Console.WriteLine("ElapsedTime - Face Generation: {0}", stopwatch.ElapsedMilliseconds); + public class ImageBufferImageData : IImageData + { + private ImageBuffer resizedImage; - return mesh; - } + public ImageBufferImageData(ImageBuffer image, double pixelWidth) + { + resizedImage = this.ToResizedGrayscale(image, pixelWidth).MirrorY(); + } - public interface IImageData - { - byte[] Pixels { get; } + public int Height => resizedImage.Height; + public byte[] Pixels => resizedImage.GetBuffer(); + public int Width => resizedImage.Width; - int Width { get; } - int Height { get; } - } + private ImageBuffer ToResizedGrayscale(ImageBuffer image, double onPlateWidth = 0) + { + var ratio = onPlateWidth / image.Width; - public class ImageBufferImageData : IImageData - { - ImageBuffer resizedImage; + var resizedImage = image.CreateScaledImage(ratio); - public ImageBufferImageData(ImageBuffer image, double pixelWidth) - { - resizedImage = this.ToResizedGrayscale(image, pixelWidth).MirrorY(); - } + var grayImage = resizedImage.ToGrayscale(); - public int Width => resizedImage.Width; - public int Height => resizedImage.Height; + // Render grayscale pixels onto resized image with larger pixel format needed by caller + resizedImage.NewGraphics2D().Render(grayImage, 0, 0); - private ImageBuffer ToResizedGrayscale(ImageBuffer image, double onPlateWidth = 0) - { - var ratio = onPlateWidth / image.Width; + return resizedImage; + } + } - var resizedImage = image.CreateScaledImage(ratio); - - var grayImage = resizedImage.ToGrayscale(); - - // Render grayscale pixels onto resized image with larger pixel format needed by caller - resizedImage.NewGraphics2D().Render(grayImage, 0, 0); - - return resizedImage; - } - - public byte[] Pixels => resizedImage.GetBuffer(); - } - } -} + private class PixelInfo + { + public Vector3 Bottom { get; set; } + public Vector3 Top { get; set; } + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Obsolete/CombineObject3D.cs b/MatterControlLib/DesignTools/Obsolete/CombineObject3D.cs index 1609d7aaa..54bb9668a 100644 --- a/MatterControlLib/DesignTools/Obsolete/CombineObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/CombineObject3D.cs @@ -36,131 +36,125 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; -using MatterHackers.PolygonMesh; using MatterHackers.PolygonMesh.Csg; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [Obsolete("Use CombineObject3D_2 instead", false)] - public class CombineObject3D : MeshWrapperObject3D - { - public CombineObject3D() - { - Name = "Combine"; - } + [Obsolete("Use CombineObject3D_2 instead", false)] + public class CombineObject3D : MeshWrapperObject3D + { + public CombineObject3D() + { + Name = "Combine"; + } - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateType); - } - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateType); + } + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); - // spin up a task to remove holes from the objects in the group - return ApplicationController.Instance.Tasks.Execute( - "Combine".Localize(), - null, - (reporter, cancellationTokenSource) => - { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + // spin up a task to remove holes from the objects in the group + return ApplicationController.Instance.Tasks.Execute( + "Combine".Localize(), + null, + (reporter, cancellationTokenSource) => + { + reporter?.Invoke(0, null); - try - { - Combine(cancellationTokenSource.Token, reporter); - } - catch - { - } + try + { + Combine(cancellationTokenSource.Token, reporter); + } + catch + { + } - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - return base.Rebuild(); - }); - } + return base.Rebuild(); + }); + } - public void Combine() - { - Combine(CancellationToken.None, null); - } + public void Combine() + { + Combine(CancellationToken.None, null); + } - public void Combine(CancellationToken cancellationToken, IProgress reporter) - { - ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); + public void Combine(CancellationToken cancellationToken, Action reporter) + { + ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); - var participants = this.Descendants().Where(o => o.OwnerID == this.ID).ToList(); - if (participants.Count() < 2) - { - return; - } + var participants = this.Descendants().Where(o => o.OwnerID == this.ID).ToList(); + if (participants.Count() < 2) + { + return; + } - var first = participants.First(); + var first = participants.First(); - var totalOperations = participants.Count() - 1; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + var totalOperations = participants.Count() - 1; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - ProgressStatus progressStatus = new ProgressStatus(); - foreach (var item in participants) - { - if (item != first) - { - var result = BooleanProcessing.Do(item.Mesh, - item.WorldMatrix(), - first.Mesh, - first.WorldMatrix(), - CsgModes.Union, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + foreach (var item in participants) + { + if (item != first) + { + var result = BooleanProcessing.Do(item.Mesh, + item.WorldMatrix(), + first.Mesh, + first.WorldMatrix(), + CsgModes.Union, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - var inverse = first.WorldMatrix(); - inverse.Invert(); - result.Transform(inverse); - using (first.RebuildLock()) - { - first.Mesh = result; - } + var inverse = first.WorldMatrix(); + inverse.Invert(); + result.Transform(inverse); + using (first.RebuildLock()) + { + first.Mesh = result; + } - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } - } - } - } + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, null); + } + } + } + } } diff --git a/MatterControlLib/DesignTools/Obsolete/CurveObject3D.cs b/MatterControlLib/DesignTools/Obsolete/CurveObject3D.cs index 0ac26a1be..4f5ce3559 100644 --- a/MatterControlLib/DesignTools/Obsolete/CurveObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/CurveObject3D.cs @@ -120,7 +120,7 @@ namespace MatterHackers.MatterControl.DesignTools { Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { Rebuild(); } diff --git a/MatterControlLib/DesignTools/Obsolete/FitToBoundsObject3D.cs b/MatterControlLib/DesignTools/Obsolete/FitToBoundsObject3D.cs index 20d367320..fe2275d38 100644 --- a/MatterControlLib/DesignTools/Obsolete/FitToBoundsObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/FitToBoundsObject3D.cs @@ -160,7 +160,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { Rebuild(); } diff --git a/MatterControlLib/DesignTools/Obsolete/IntersectionObject3D.cs b/MatterControlLib/DesignTools/Obsolete/IntersectionObject3D.cs index c8612d15a..a561938f7 100644 --- a/MatterControlLib/DesignTools/Obsolete/IntersectionObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/IntersectionObject3D.cs @@ -32,123 +32,117 @@ either expressed or implied, of the FreeBSD Project. /************************ USE NEWER VERSION **************************/ /*********************************************************************/ +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.PolygonMesh.Csg; using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MatterHackers.Agg; -using MatterHackers.Agg.UI; -using MatterHackers.DataConverters3D; -using MatterHackers.Localizations; -using MatterHackers.PolygonMesh; -using MatterHackers.PolygonMesh.Csg; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [Obsolete("Use IntersectionObject3D_2 instead", false)] - public class IntersectionObject3D : MeshWrapperObject3D - { - public IntersectionObject3D() - { - Name = "Intersection"; - } + [Obsolete("Use IntersectionObject3D_2 instead", false)] + public class IntersectionObject3D : MeshWrapperObject3D + { + public IntersectionObject3D() + { + Name = "Intersection"; + } - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateType); - } - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateType); + } + } - public override Task Rebuild() - { - var rebuildLocks = this.RebuilLockAll(); + public override Task Rebuild() + { + var rebuildLocks = this.RebuilLockAll(); - return ApplicationController.Instance.Tasks.Execute("Intersection".Localize(), null, (reporter, cancellationTokenSource) => - { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + return ApplicationController.Instance.Tasks.Execute("Intersection".Localize(), null, (reporter, cancellationTokenSource) => + { + reporter?.Invoke(0, null); - try - { - Intersect(cancellationTokenSource.Token, reporter); - } - catch - { - } + try + { + Intersect(cancellationTokenSource.Token, reporter); + } + catch + { + } - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); - return base.Rebuild(); - }); - } + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); + return base.Rebuild(); + }); + } - private void Intersect(CancellationToken cancellationToken, IProgress reporter) - { - ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); + private void Intersect(CancellationToken cancellationToken, Action reporter) + { + ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); - var participants = this.DescendantsAndSelf().Where((obj) => obj.OwnerID == this.ID); + var participants = this.DescendantsAndSelf().Where((obj) => obj.OwnerID == this.ID); - if (participants.Count() > 1) - { - var first = participants.First(); + if (participants.Count() > 1) + { + var first = participants.First(); - var totalOperations = participants.Count() - 1; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + var totalOperations = participants.Count() - 1; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - ProgressStatus progressStatus = new ProgressStatus(); - foreach (var remove in participants) - { - if (remove != first) - { - var result = BooleanProcessing.Do(remove.Mesh, - remove.WorldMatrix(), - first.Mesh, - first.WorldMatrix(), - CsgModes.Intersect, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + foreach (var remove in participants) + { + if (remove != first) + { + var result = BooleanProcessing.Do(remove.Mesh, + remove.WorldMatrix(), + first.Mesh, + first.WorldMatrix(), + CsgModes.Intersect, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - var inverse = first.WorldMatrix(); - inverse.Invert(); - result.Transform(inverse); - using (first.RebuildLock()) - { - first.Mesh = result; - } - remove.Visible = false; + var inverse = first.WorldMatrix(); + inverse.Invert(); + result.Transform(inverse); + using (first.RebuildLock()) + { + first.Mesh = result; + } + remove.Visible = false; - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter.Report(progressStatus); - } - } - } - } - } -} + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, null); + } + } + } + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Obsolete/PinchObject3D.cs b/MatterControlLib/DesignTools/Obsolete/PinchObject3D.cs index 0b2c98d55..16aad411e 100644 --- a/MatterControlLib/DesignTools/Obsolete/PinchObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/PinchObject3D.cs @@ -69,7 +69,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Obsolete/RotateObject3D.cs b/MatterControlLib/DesignTools/Obsolete/RotateObject3D.cs index 8dd2fc683..5ae10c506 100644 --- a/MatterControlLib/DesignTools/Obsolete/RotateObject3D.cs +++ b/MatterControlLib/DesignTools/Obsolete/RotateObject3D.cs @@ -113,7 +113,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/AlignObject3D.cs b/MatterControlLib/DesignTools/Operations/AlignObject3D.cs index ec9ca81a5..911dc5afe 100644 --- a/MatterControlLib/DesignTools/Operations/AlignObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/AlignObject3D.cs @@ -326,7 +326,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations NameOverriden = false; base.OnInvalidate(invalidateArgs); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/AlignObject3D_2.cs b/MatterControlLib/DesignTools/Operations/AlignObject3D_2.cs index af57650ca..60641b20a 100644 --- a/MatterControlLib/DesignTools/Operations/AlignObject3D_2.cs +++ b/MatterControlLib/DesignTools/Operations/AlignObject3D_2.cs @@ -258,7 +258,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations NameOverriden = false; base.OnInvalidate(invalidateArgs); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/ArrayObject3D.cs b/MatterControlLib/DesignTools/Operations/ArrayObject3D.cs index d410b1b4d..801f636a1 100644 --- a/MatterControlLib/DesignTools/Operations/ArrayObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/ArrayObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,94 +32,94 @@ using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.DesignTools.Operations { - public abstract class ArrayObject3D : OperationSourceContainerObject3D - { - public abstract IntOrExpression Count { get; set; } + public abstract class ArrayObject3D : OperationSourceContainerObject3D + { + public abstract IntOrExpression Count { get; set; } public override void Apply(Agg.UI.UndoBuffer undoBuffer) { - var indexExpansions = new (string key, int index)[] - { - ("[index]", 0), - ("[index0]", 0), - ("[index1]", 1), - ("[index2]", 2), - }; + var indexExpansions = new (string key, int index)[] + { + ("[index]", 0), + ("[index0]", 0), + ("[index1]", 1), + ("[index2]", 2), + }; - // convert [index] expressions to their constant values - foreach (var item in this.Descendants((item) => !(item is ArrayObject3D))) - { - foreach (var expansion in indexExpansions) - { - foreach (var expression in SheetObject3D.GetActiveExpressions(item, expansion.key, false)) - { - var expressionValue = expression.Expression; - expression.Expression = expressionValue.Replace(expansion.key, SheetObject3D.RetrieveArrayIndex(item, expansion.index).ToString()); - } + // convert [index] expressions to their constant values + foreach (var item in this.Descendants((item) => !(item is ArrayObject3D))) + { + foreach (var expansion in indexExpansions) + { + foreach (var expression in Expressions.GetActiveExpression(item, expansion.key, false)) + { + var expressionValue = expression.Expression; + expression.Expression = expressionValue.Replace(expansion.key, Expressions.RetrieveArrayIndex(item, expansion.index).ToString()); + } - // Also convert index expressions in ComponentObjects to their constants - if (item is ComponentObject3D component) - { - for (int i = 0; i < component.SurfacedEditors.Count; i++) - { - var (cellId, cellData) = component.DecodeContent(i); + // Also convert index expressions in ComponentObjects to their constants + if (item is ComponentObject3D component) + { + for (int i = 0; i < component.SurfacedEditors.Count; i++) + { + var (cellId, cellData) = component.DecodeContent(i); - if (cellId != null) - { - var newValue = cellData.Replace(expansion.key, SheetObject3D.RetrieveArrayIndex(component, expansion.index).ToString()); - component.SurfacedEditors[i] = "!" + cellId + "," + newValue; - } - } - } - } - } + if (cellId != null) + { + var newValue = cellData.Replace(expansion.key, Expressions.RetrieveArrayIndex(component, expansion.index).ToString()); + component.SurfacedEditors[i] = "!" + cellId + "," + newValue; + } + } + } + } + } - // then call base apply - base.Apply(undoBuffer); + // then call base apply + base.Apply(undoBuffer); } internal void ProcessIndexExpressions() { - var updateItems = SheetObject3D.SortAndLockUpdateItems(this, (item) => - { - if (!SheetObject3D.HasExpressionWithString(item, "=", true)) - { - return false; - } - - // WIP - if (item.Parent == this) - { - // only process our children that are not the source object - return !(item is OperationSourceObject3D); - } - else if (item.Parent is OperationSourceContainerObject3D) - { - // If we find another source container - // Only process its children that are the source container (they will be replicated and modified correctly by the source container) - return item is OperationSourceObject3D; - } - else if (item.Parent is OperationSourceObject3D operationSourceObject3D - && operationSourceObject3D.Parent == this) - { - // we don't need to rebuild our source object - return false; - } - else if (item.Parent is ComponentObject3D) + var updateItems = Expressions.SortAndLockUpdateItems(this, (item) => + { + if (!Expressions.HasExpressionWithString(item, "=", true)) { - return false; + return false; } - // process everything else - return true; - }, true); + // WIP + if (item.Parent == this) + { + // only process our children that are not the source object + return !(item is OperationSourceObject3D); + } + else if (item.Parent is OperationSourceContainerObject3D) + { + // If we find another source container + // Only process its children that are the source container (they will be replicated and modified correctly by the source container) + return item is OperationSourceObject3D; + } + else if (item.Parent is OperationSourceObject3D operationSourceObject3D + && operationSourceObject3D.Parent == this) + { + // we don't need to rebuild our source object + return false; + } + else if (item.Parent is ComponentObject3D) + { + return false; + } - var runningInterval = SheetObject3D.SendInvalidateInRebuildOrder(updateItems, InvalidateType.Properties); + // process everything else + return true; + }, true); - while (runningInterval.Active) - { - Thread.Sleep(10); - } - } - } + var runningInterval = Expressions.SendInvalidateInRebuildOrder(updateItems, InvalidateType.Properties); + + while (runningInterval.Active) + { + Thread.Sleep(10); + } + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/CurveObject3D_2.cs b/MatterControlLib/DesignTools/Operations/CurveObject3D_2.cs index 29204b4be..dec26a421 100644 --- a/MatterControlLib/DesignTools/Operations/CurveObject3D_2.cs +++ b/MatterControlLib/DesignTools/Operations/CurveObject3D_2.cs @@ -51,215 +51,209 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.DesignTools { - [Obsolete("Use CurveObject3D_3 instead", false)] - public class CurveObject3D_2 : OperationSourceContainerObject3D, IEditorDraw - { - public CurveObject3D_2() - { - Name = "Curve".Localize(); - } + [Obsolete("Use CurveObject3D_3 instead", false)] + public class CurveObject3D_2 : OperationSourceContainerObject3D, IEditorDraw + { + public CurveObject3D_2() + { + Name = "Curve".Localize(); + } - [DisplayName("Bend Up")] - public bool BendCcw { get; set; } = true; + [DisplayName("Bend Up")] + public bool BendCcw { get; set; } = true; - public double Diameter { get; set; } = double.MaxValue; + public double Diameter { get; set; } = double.MaxValue; - [Slider(3, 360)] - [Description("Ensures the rotated part has a minimum number of sides per complete rotation")] - public double MinSidesPerRotation { get; set; } = 30; + [Slider(3, 360)] + [Description("Ensures the rotated part has a minimum number of sides per complete rotation")] + public double MinSidesPerRotation { get; set; } = 30; - [Slider(0, 100)] - [Description("Where to start the bend as a percent of the width of the part")] - public double StartPercent { get; set; } = 50; + [Description("Split the mesh so it has enough geometry to create a smooth curve")] + public bool SplitMesh { get; set; } = true; - [Description("Split the mesh so it has enough geometry to create a smooth curve")] - public bool SplitMesh { get; set; } = true; + [Slider(0, 100)] + [Description("Where to start the bend as a percent of the width of the part")] + public double StartPercent { get; set; } = 50; + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + var drawInfo = GetDrawInfo(); - struct DrawInfo - { - public AxisAlignedBoundingBox sourceAabb; - public double distance; - public Vector3 center; - } + // render the top and bottom rings + layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, Diameter, drawInfo.sourceAabb.ZSize, 100, Color.Red, Color.Transparent); - DrawInfo GetDrawInfo() - { - var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - var distance = Diameter / 2 + sourceAabb.YSize / 2; - var center = sourceAabb.Center + new Vector3(0, BendCcw ? distance : -distance, 0); - center.X -= sourceAabb.XSize / 2 - (StartPercent / 100.0) * sourceAabb.XSize; + // render the split lines + var radius = Diameter / 2; + var circumference = MathHelper.Tau * radius; + var xxx = drawInfo.sourceAabb.XSize * (StartPercent / 100.0); + var startAngle = MathHelper.Tau * 3 / 4 - xxx / circumference * MathHelper.Tau; + layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, Diameter, drawInfo.sourceAabb.ZSize, (int)Math.Max(0, Math.Min(100, this.MinSidesPerRotation)), Color.Transparent, Color.Red, phase: startAngle); - return new DrawInfo - { - sourceAabb = sourceAabb, - distance = distance, - center = center, - }; - } + // turn the lighting back on + GL.Enable(EnableCap.Lighting); + } - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - var drawInfo = GetDrawInfo(); + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + var drawInfo = GetDrawInfo(); + var radius = Diameter / 2; + var halfHeight = drawInfo.sourceAabb.ZSize / 2; + return AxisAlignedBoundingBox.CenteredBox(new Vector3(radius, radius, halfHeight), drawInfo.center).NewTransformed(this.WorldMatrix()); + } - // render the top and bottom rings - layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, Diameter, drawInfo.sourceAabb.ZSize, 100, Color.Red, Color.Transparent); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - // render the split lines - var radius = Diameter / 2; - var circumference = MathHelper.Tau * radius; - var xxx = drawInfo.sourceAabb.XSize * (StartPercent / 100.0); - var startAngle = MathHelper.Tau * 3 / 4 - xxx / circumference * MathHelper.Tau; - layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, Diameter, drawInfo.sourceAabb.ZSize, (int)Math.Max(0, Math.Min(100, this.MinSidesPerRotation)), Color.Transparent, Color.Red, phase: startAngle); + bool valuesChanged = false; - // turn the lighting back on - GL.Enable(EnableCap.Lighting); - } + // ensure we have good values + StartPercent = Util.Clamp(StartPercent, 0, 100, ref valuesChanged); - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - var drawInfo = GetDrawInfo(); - var radius = Diameter / 2; - var halfHeight = drawInfo.sourceAabb.ZSize / 2; - return AxisAlignedBoundingBox.CenteredBox(new Vector3(radius, radius, halfHeight), drawInfo.center).NewTransformed(this.WorldMatrix()); - } + if (Diameter < 1 || Diameter > 100000) + { + if (Diameter == double.MaxValue) + { + var aabb = this.GetAxisAlignedBoundingBox(); + // uninitialized set to a reasonable value + Diameter = (int)aabb.XSize; + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + Diameter = Math.Min(100000, Math.Max(1, Diameter)); + valuesChanged = true; + } - bool valuesChanged = false; + MinSidesPerRotation = Util.Clamp(MinSidesPerRotation, 3, 360, ref valuesChanged); - // ensure we have good values - StartPercent = agg_basics.Clamp(StartPercent, 0, 100, ref valuesChanged); + var rebuildLocks = this.RebuilLockAll(); - if (Diameter < 1 || Diameter > 100000) - { - if (Diameter == double.MaxValue) - { - var aabb = this.GetAxisAlignedBoundingBox(); - // uninitialized set to a reasonable value - Diameter = (int)aabb.XSize; - } + return ApplicationController.Instance.Tasks.Execute( + "Curve".Localize(), + null, + (reporter, cancellationToken) => + { + var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - Diameter = Math.Min(100000, Math.Max(1, Diameter)); - valuesChanged = true; - } + var radius = Diameter / 2; + var circumference = MathHelper.Tau * radius; + double numRotations = sourceAabb.XSize / circumference; + double numberOfCuts = numRotations * MinSidesPerRotation; + double cutSize = sourceAabb.XSize / numberOfCuts; + double cutPosition = sourceAabb.MinXYZ.X + cutSize; + var cuts = new List(); + for (int i = 0; i < numberOfCuts; i++) + { + cuts.Add(cutPosition); + cutPosition += cutSize; + } - MinSidesPerRotation = agg_basics.Clamp(MinSidesPerRotation, 3, 360, ref valuesChanged); + var rotationCenter = new Vector3(sourceAabb.MinXYZ.X + (sourceAabb.MaxXYZ.X - sourceAabb.MinXYZ.X) * (StartPercent / 100), + BendCcw ? sourceAabb.MaxXYZ.Y + radius : sourceAabb.MinXYZ.Y - radius, + sourceAabb.Center.Z); - var rebuildLocks = this.RebuilLockAll(); + var curvedChildren = new List(); - return ApplicationController.Instance.Tasks.Execute( - "Curve".Localize(), - null, - (reporter, cancellationToken) => - { - var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + foreach (var sourceItem in SourceContainer.VisibleMeshes()) + { + var originalMesh = sourceItem.Mesh; + reporter?.Invoke(0, "Copy Mesh".Localize()); + var transformedMesh = originalMesh.Copy(CancellationToken.None); + var itemMatrix = sourceItem.WorldMatrix(SourceContainer); - var radius = Diameter / 2; - var circumference = MathHelper.Tau * radius; - double numRotations = sourceAabb.XSize / circumference; - double numberOfCuts = numRotations * MinSidesPerRotation; - double cutSize = sourceAabb.XSize / numberOfCuts; - double cutPosition = sourceAabb.MinXYZ.X + cutSize; - var cuts = new List(); - for (int i = 0; i < numberOfCuts; i++) - { - cuts.Add(cutPosition); - cutPosition += cutSize; - } + // transform into this space + transformedMesh.Transform(itemMatrix); - var rotationCenter = new Vector3(sourceAabb.MinXYZ.X + (sourceAabb.MaxXYZ.X - sourceAabb.MinXYZ.X) * (StartPercent / 100), - BendCcw ? sourceAabb.MaxXYZ.Y + radius : sourceAabb.MinXYZ.Y - radius, - sourceAabb.Center.Z); + if (SplitMesh) + { + reporter?.Invoke(0, "Split Mesh".Localize()); - var curvedChildren = new List(); + // split the mesh along the x axis + transformedMesh.SplitOnPlanes(Vector3.UnitX, cuts, cutSize / 8); + } - var status = new ProgressStatus(); + for (int i = 0; i < transformedMesh.Vertices.Count; i++) + { + var position = transformedMesh.Vertices[i]; - foreach (var sourceItem in SourceContainer.VisibleMeshes()) - { - var originalMesh = sourceItem.Mesh; - status.Status = "Copy Mesh".Localize(); - reporter.Report(status); - var transformedMesh = originalMesh.Copy(CancellationToken.None); - var itemMatrix = sourceItem.WorldMatrix(SourceContainer); + var angleToRotate = ((position.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; + var distanceFromCenter = rotationCenter.Y - position.Y; + if (!BendCcw) + { + angleToRotate = -angleToRotate; + distanceFromCenter = -distanceFromCenter; + } - // transform into this space - transformedMesh.Transform(itemMatrix); + var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; + rotatePosition.Z = position.Z; + transformedMesh.Vertices[i] = rotatePosition + new Vector3Float(rotationCenter.X, radius + sourceAabb.MaxXYZ.Y, 0); + } - if (SplitMesh) - { - status.Status = "Split Mesh".Localize(); - reporter.Report(status); + // transform back into item local space + transformedMesh.Transform(Matrix4X4.CreateTranslation(-rotationCenter) * itemMatrix.Inverted); - // split the mesh along the x axis - transformedMesh.SplitOnPlanes(Vector3.UnitX, cuts, cutSize / 8); - } + if (SplitMesh) + { + reporter?.Invoke(0, "Merge Vertices".Localize()); - for (int i = 0; i < transformedMesh.Vertices.Count; i++) - { - var position = transformedMesh.Vertices[i]; + transformedMesh.MergeVertices(.1); + } - var angleToRotate = ((position.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; - var distanceFromCenter = rotationCenter.Y - position.Y; - if (!BendCcw) - { - angleToRotate = -angleToRotate; - distanceFromCenter = -distanceFromCenter; - } + transformedMesh.CalculateNormals(); - var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; - rotatePosition.Z = position.Z; - transformedMesh.Vertices[i] = rotatePosition + new Vector3Float(rotationCenter.X, radius + sourceAabb.MaxXYZ.Y, 0); - } + var curvedChild = new Object3D() + { + Mesh = transformedMesh + }; + curvedChild.CopyWorldProperties(sourceItem, SourceContainer, Object3DPropertyFlags.All, false); + curvedChild.Visible = true; + curvedChild.Translate(new Vector3(rotationCenter)); + if (!BendCcw) + { + curvedChild.Translate(0, -sourceAabb.YSize - Diameter, 0); + } - // transform back into item local space - transformedMesh.Transform(Matrix4X4.CreateTranslation(-rotationCenter) * itemMatrix.Inverted); + curvedChildren.Add(curvedChild); + } - if (SplitMesh) - { - status.Status = "Merge Vertices".Localize(); - reporter.Report(status); + RemoveAllButSource(); + this.SourceContainer.Visible = false; - transformedMesh.MergeVertices(.1); - } + this.Children.Modify((list) => + { + list.AddRange(curvedChildren); + }); - transformedMesh.CalculateNormals(); + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + Invalidate(InvalidateType.DisplayValues); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - var curvedChild = new Object3D() - { - Mesh = transformedMesh - }; - curvedChild.CopyWorldProperties(sourceItem, SourceContainer, Object3DPropertyFlags.All, false); - curvedChild.Visible = true; - curvedChild.Translate(new Vector3(rotationCenter)); - if (!BendCcw) - { - curvedChild.Translate(0, -sourceAabb.YSize - Diameter, 0); - } + return Task.CompletedTask; + }); + } - curvedChildren.Add(curvedChild); - } + DrawInfo GetDrawInfo() + { + var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + var distance = Diameter / 2 + sourceAabb.YSize / 2; + var center = sourceAabb.Center + new Vector3(0, BendCcw ? distance : -distance, 0); + center.X -= sourceAabb.XSize / 2 - (StartPercent / 100.0) * sourceAabb.XSize; - RemoveAllButSource(); - this.SourceContainer.Visible = false; + return new DrawInfo + { + sourceAabb = sourceAabb, + distance = distance, + center = center, + }; + } - this.Children.Modify((list) => - { - list.AddRange(curvedChildren); - }); - - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - Invalidate(InvalidateType.DisplayValues); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); - - return Task.CompletedTask; - }); - } - } + struct DrawInfo + { + public Vector3 center; + public double distance; + public AxisAlignedBoundingBox sourceAabb; + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs b/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs index fed67d14f..a0706cce7 100644 --- a/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs +++ b/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -48,343 +48,338 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.DesignTools { - public class CurveObject3D_3 : OperationSourceContainerObject3D, IPropertyGridModifier, IEditorDraw - { - public CurveObject3D_3() - { - Name = "Curve".Localize(); - } + public class CurveObject3D_3 : OperationSourceContainerObject3D, IPropertyGridModifier, IEditorDraw + { + public CurveObject3D_3() + { + Name = "Curve".Localize(); + } - public enum BendDirections - { - Bend_Up, - Bend_Down, - } + public enum BendDirections + { + Bend_Up, + Bend_Down, + } - public enum BendTypes - { - [Description("Bend the part by an angle")] - Angle, - [Description("Bend the part around a specified diameter")] - Diameter, - } + public enum BendTypes + { + [Description("Bend the part by an angle")] + Angle, + [Description("Bend the part around a specified diameter")] + Diameter, + } - [HideFromEditor] - public Vector3 PostCurveOffset { get; set; } = new Vector3(); + [HideFromEditor] + public Vector3 PostCurveOffset { get; set; } = new Vector3(); - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Tabs)] - public BendTypes BendType { get; set; } = BendTypes.Angle; - - - [MaxDecimalPlaces(2)] - [Description("Set the radius that the bend will wrap around")] - [DescriptionImage("https://lh3.googleusercontent.com/PpQKIIOqD-49UMhgM_HCvig9Mw_UtUwO08UoRVSLJlCv9h5cGBLMvaXbtORrVQrWYPcKZ4_DfrDoKfcu2TuyYVQOl3AeZNoYflgnijc")] - [Slider(1, 400, Easing.EaseType.Quadratic, snapDistance: 1)] - public DoubleOrExpression Diameter { get; set; } = double.MaxValue; - - [MaxDecimalPlaces(1)] - [Description("Set the angle of the curvature")] - [DescriptionImage("https://lh3.googleusercontent.com/TYe-CZfwJMKvP2JWBQihkvHD1PyB_nvyf0h3DhvyJu1RBjQWgqeOEsSH3sYcwA4alJjJmziueYGCbB_mic_QoYKuhKrmipkV2eG4_A")] - [Slider(1, 360, snapDistance: 1)] - public DoubleOrExpression Angle { get; set; } = 90; - - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - [Description("The part will bend around the z axis either up or down")] - [DescriptionImage("https://lh3.googleusercontent.com/h-s2FyBKO5etYDr_9YSLtGmGmQTcmSGMu4p0mRqX4_7Z62Ndn2QRLoFICC6X9scbhr1EP29RiYRj4EmhLMUwiNTAG-PIiFbzI_jAses")] - public BendDirections BendDirection { get; set; } = BendDirections.Bend_Up; - - [Slider(0, 100, snapDistance: 1)] - [Description("Where to start the bend as a percent from the left side")] - [DescriptionImage("https://lh3.googleusercontent.com/eOeWjr98uz_E924PnNaXrasepv15nWEuvhqH-jbaQyvrOVdX5MHXF00HdZQGC8NLpJc9ok1sToMtyPx1wnnDgFwTTGA5MjoMFu612AY1")] - public DoubleOrExpression StartPercent { get; set; } = 50; - - [DescriptionImage("https://lh3.googleusercontent.com/arAJFTHAOPKn9BQtm1xEyct4LuA2jUAxW11q4cdQPz_JfoCTjS1rxtVTUdE1ND0Q_eigUa27Yc28U08zY2LDiQgS7kKkXKY_FY838p-5")] - [Description("Split the mesh so it has enough geometry to create a smooth curve")] - public bool SplitMesh { get; set; } = true; - - [Slider(3, 360, Easing.EaseType.Cubic, snapDistance: 1)] - [Description("Ensures the rotated part has a minimum number of sides per complete rotation")] - [DescriptionImage("https://lh3.googleusercontent.com/p9MyKu3AFP55PnobUKZQPqf6iAx11GzXyX-25f1ddrUnfCt8KFGd1YtHOR5HqfO0mhlX2ZVciZV4Yn0Kzfm43SErOS_xzgsESTu9scux")] - public DoubleOrExpression MinSidesPerRotation { get; set; } = 30; - - struct DrawInfo - { - public double diameter; - public double startPercent; - public AxisAlignedBoundingBox sourceAabb; - public double distance; - public Vector3 center; - } - - DrawInfo GetDrawInfo() - { - var diameter = Diameter.Value(this); - var startPercent = StartPercent.Value(this); - - var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - var distance = diameter / 2 + sourceAabb.YSize / 2; - var center = sourceAabb.Center + new Vector3(0, BendDirection == BendDirections.Bend_Up ? distance : -distance, 0); - center.X -= sourceAabb.XSize / 2 - (startPercent / 100.0) * sourceAabb.XSize; - center = Vector3.Zero;//.Transform(Matrix.Inverted); - - return new DrawInfo - { - diameter = diameter, - startPercent = startPercent, - sourceAabb = sourceAabb, - distance = distance, - center = center, - }; - } - - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - var drawInfo = GetDrawInfo(); - var minSidesPerRotation = MinSidesPerRotation.Value(this); - - // render the top and bottom rings - layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, drawInfo.diameter, drawInfo.sourceAabb.ZSize, 100, Color.Red, Color.Transparent); - - // render the split lines - var radius = drawInfo.diameter / 2; - var circumference = MathHelper.Tau * radius; - var xxx = drawInfo.sourceAabb.XSize * (drawInfo.startPercent / 100.0); - var startAngle = MathHelper.Tau * 3 / 4 - xxx / circumference * MathHelper.Tau; - layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, drawInfo.diameter, drawInfo.sourceAabb.ZSize, (int)Math.Max(0, Math.Min(100, minSidesPerRotation)), Color.Transparent, Color.Red, phase: startAngle); - - // turn the lighting back on - GL.Enable(EnableCap.Lighting); - } - - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - var drawInfo = GetDrawInfo(); - return AxisAlignedBoundingBox.CenteredBox(new Vector3(drawInfo.diameter, drawInfo.diameter, drawInfo.sourceAabb.ZSize), drawInfo.center).NewTransformed(this.WorldMatrix()); - } - - private double DiameterFromAngle() - { - var diameter = Diameter.Value(this); - var angle = Angle.Value(this); - - var aabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - var angleR = MathHelper.DegreesToRadians(angle); - var ratio = angleR / MathHelper.Tau; - var newDiameter = (aabb.XSize / ratio) / Math.PI; - if (Math.Abs(diameter - newDiameter) > .0001) - { - Diameter = newDiameter; - Invalidate(InvalidateType.DisplayValues); - } - - return diameter; - } + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Tabs)] + public BendTypes BendType { get; set; } = BendTypes.Angle; - private void AngleFromDiameter() - { - var diameter = Diameter.Value(this); - var angle = Angle.Value(this); + [MaxDecimalPlaces(2)] + [Description("Set the radius that the bend will wrap around")] + [DescriptionImage("https://lh3.googleusercontent.com/PpQKIIOqD-49UMhgM_HCvig9Mw_UtUwO08UoRVSLJlCv9h5cGBLMvaXbtORrVQrWYPcKZ4_DfrDoKfcu2TuyYVQOl3AeZNoYflgnijc")] + [Slider(1, 400, Easing.EaseType.Quadratic, snapDistance: 1)] + public DoubleOrExpression Diameter { get; set; } = double.MaxValue; - var aabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - var ratio = aabb.XSize / (MathHelper.Tau * diameter / 2); - var angleR = MathHelper.Tau * ratio; - var newAngle = MathHelper.RadiansToDegrees(angleR); - if (Math.Abs(angle - newAngle) > .00001) - { - Angle = MathHelper.RadiansToDegrees(angleR); - Invalidate(InvalidateType.DisplayValues); - } - } + [MaxDecimalPlaces(1)] + [Description("Set the angle of the curvature")] + [DescriptionImage("https://lh3.googleusercontent.com/TYe-CZfwJMKvP2JWBQihkvHD1PyB_nvyf0h3DhvyJu1RBjQWgqeOEsSH3sYcwA4alJjJmziueYGCbB_mic_QoYKuhKrmipkV2eG4_A")] + [Slider(1, 360, snapDistance: 1)] + public DoubleOrExpression Angle { get; set; } = 90; - private CancellationTokenSource cancellationToken; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + [Description("The part will bend around the z axis either up or down")] + [DescriptionImage("https://lh3.googleusercontent.com/h-s2FyBKO5etYDr_9YSLtGmGmQTcmSGMu4p0mRqX4_7Z62Ndn2QRLoFICC6X9scbhr1EP29RiYRj4EmhLMUwiNTAG-PIiFbzI_jAses")] + public BendDirections BendDirection { get; set; } = BendDirections.Bend_Up; - public bool IsBuilding => this.cancellationToken != null; + [Slider(0, 100, snapDistance: 1)] + [Description("Where to start the bend as a percent from the left side")] + [DescriptionImage("https://lh3.googleusercontent.com/eOeWjr98uz_E924PnNaXrasepv15nWEuvhqH-jbaQyvrOVdX5MHXF00HdZQGC8NLpJc9ok1sToMtyPx1wnnDgFwTTGA5MjoMFu612AY1")] + public DoubleOrExpression StartPercent { get; set; } = 50; - public void CancelBuild() - { - var threadSafe = this.cancellationToken; - if (threadSafe != null) - { - threadSafe.Cancel(); - } - } + [DescriptionImage("https://lh3.googleusercontent.com/arAJFTHAOPKn9BQtm1xEyct4LuA2jUAxW11q4cdQPz_JfoCTjS1rxtVTUdE1ND0Q_eigUa27Yc28U08zY2LDiQgS7kKkXKY_FY838p-5")] + [Description("Split the mesh so it has enough geometry to create a smooth curve")] + public bool SplitMesh { get; set; } = true; + + [Slider(3, 360, Easing.EaseType.Cubic, snapDistance: 1)] + [Description("Ensures the rotated part has a minimum number of sides per complete rotation")] + [DescriptionImage("https://lh3.googleusercontent.com/p9MyKu3AFP55PnobUKZQPqf6iAx11GzXyX-25f1ddrUnfCt8KFGd1YtHOR5HqfO0mhlX2ZVciZV4Yn0Kzfm43SErOS_xzgsESTu9scux")] + public DoubleOrExpression MinSidesPerRotation { get; set; } = 30; + + struct DrawInfo + { + public double diameter; + public double startPercent; + public AxisAlignedBoundingBox sourceAabb; + public double distance; + public Vector3 center; + } + + DrawInfo GetDrawInfo() + { + var diameter = Diameter.Value(this); + var startPercent = StartPercent.Value(this); + + var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + var distance = diameter / 2 + sourceAabb.YSize / 2; + var center = sourceAabb.Center + new Vector3(0, BendDirection == BendDirections.Bend_Up ? distance : -distance, 0); + center.X -= sourceAabb.XSize / 2 - (startPercent / 100.0) * sourceAabb.XSize; + center = Vector3.Zero;//.Transform(Matrix.Inverted); + + return new DrawInfo + { + diameter = diameter, + startPercent = startPercent, + sourceAabb = sourceAabb, + distance = distance, + center = center, + }; + } + + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + var drawInfo = GetDrawInfo(); + var minSidesPerRotation = MinSidesPerRotation.Value(this); + + // render the top and bottom rings + layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, drawInfo.diameter, drawInfo.sourceAabb.ZSize, 100, Color.Red, Color.Transparent); + + // render the split lines + var radius = drawInfo.diameter / 2; + var circumference = MathHelper.Tau * radius; + var xxx = drawInfo.sourceAabb.XSize * (drawInfo.startPercent / 100.0); + var startAngle = MathHelper.Tau * 3 / 4 - xxx / circumference * MathHelper.Tau; + layer.World.RenderCylinderOutline(this.WorldMatrix(), drawInfo.center, drawInfo.diameter, drawInfo.sourceAabb.ZSize, (int)Math.Max(0, Math.Min(100, minSidesPerRotation)), Color.Transparent, Color.Red, phase: startAngle); + + // turn the lighting back on + GL.Enable(EnableCap.Lighting); + } + + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + var drawInfo = GetDrawInfo(); + return AxisAlignedBoundingBox.CenteredBox(new Vector3(drawInfo.diameter, drawInfo.diameter, drawInfo.sourceAabb.ZSize), drawInfo.center).NewTransformed(this.WorldMatrix()); + } + + private double DiameterFromAngle() + { + var diameter = Diameter.Value(this); + var angle = Angle.Value(this); + + var aabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + var angleR = MathHelper.DegreesToRadians(angle); + var ratio = angleR / MathHelper.Tau; + var newDiameter = (aabb.XSize / ratio) / Math.PI; + if (Math.Abs(diameter - newDiameter) > .0001) + { + Diameter = newDiameter; + Invalidate(InvalidateType.DisplayValues); + } + + return diameter; + } + + + private void AngleFromDiameter() + { + var diameter = Diameter.Value(this); + var angle = Angle.Value(this); + + var aabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + var ratio = aabb.XSize / (MathHelper.Tau * diameter / 2); + var angleR = MathHelper.Tau * ratio; + var newAngle = MathHelper.RadiansToDegrees(angleR); + if (Math.Abs(angle - newAngle) > .00001) + { + Angle = MathHelper.RadiansToDegrees(angleR); + Invalidate(InvalidateType.DisplayValues); + } + } + + private CancellationTokenSource cancellationToken; + + public bool IsBuilding => this.cancellationToken != null; + + public void CancelBuild() + { + var threadSafe = this.cancellationToken; + if (threadSafe != null) + { + threadSafe.Cancel(); + } + } public override void Cancel(UndoBuffer undoBuffer) { - this.Matrix *= Matrix4X4.CreateTranslation(-PostCurveOffset); - base.Cancel(undoBuffer); + this.Matrix *= Matrix4X4.CreateTranslation(-PostCurveOffset); + base.Cancel(undoBuffer); } public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + { + this.DebugDepth("Rebuild"); - bool valuesChanged = false; + bool valuesChanged = false; - // ensure we have good values - var startPercent = StartPercent.ClampIfNotCalculated(this, 0, 100, ref valuesChanged); + // ensure we have good values + var startPercent = StartPercent.ClampIfNotCalculated(this, 0, 100, ref valuesChanged); - var diameter = Diameter.Value(this); - if (diameter == double.MaxValue - || diameter == 0) - { - diameter = DiameterFromAngle(); - } + var diameter = Diameter.Value(this); + if (diameter == double.MaxValue + || diameter == 0) + { + diameter = DiameterFromAngle(); + } - // keep the unused type synced so we don't change the bend when clicking the tabs - if (BendType == BendTypes.Diameter) - { - AngleFromDiameter(); - } - else - { - diameter = DiameterFromAngle(); - } + // keep the unused type synced so we don't change the bend when clicking the tabs + if (BendType == BendTypes.Diameter) + { + AngleFromDiameter(); + } + else + { + diameter = DiameterFromAngle(); + } - diameter = Diameter.ClampIfNotCalculated(this, .1, 100000, ref valuesChanged); + diameter = Diameter.ClampIfNotCalculated(this, .1, 100000, ref valuesChanged); - var minSidesPerRotation = MinSidesPerRotation.ClampIfNotCalculated(this, 3, 360, ref valuesChanged); + var minSidesPerRotation = MinSidesPerRotation.ClampIfNotCalculated(this, 3, 360, ref valuesChanged); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); - return ApplicationController.Instance.Tasks.Execute( - "Curve".Localize(), - null, - (reporter, cancellationTokenSource) => - { - this.cancellationToken = cancellationTokenSource; - var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); + return ApplicationController.Instance.Tasks.Execute( + "Curve".Localize(), + null, + (reporter, cancellationTokenSource) => + { + this.cancellationToken = cancellationTokenSource; + var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); - // If this is the first build (the only child is the source container), fix the aabb. - var firstBuild = this.Children.Count == 1; - var initialAabb = this.GetAxisAlignedBoundingBox(); + // If this is the first build (the only child is the source container), fix the aabb. + var firstBuild = this.Children.Count == 1; + var initialAabb = this.GetAxisAlignedBoundingBox(); - var radius = diameter / 2; - var circumference = MathHelper.Tau * radius; - double numRotations = sourceAabb.XSize / circumference; - double numberOfCuts = numRotations * minSidesPerRotation; - double cutSize = sourceAabb.XSize / numberOfCuts; - double cutPosition = sourceAabb.MinXYZ.X + cutSize; - var cuts = new List(); - for (int i = 0; i < numberOfCuts; i++) - { - cuts.Add(cutPosition); - cutPosition += cutSize; - } + var radius = diameter / 2; + var circumference = MathHelper.Tau * radius; + double numRotations = sourceAabb.XSize / circumference; + double numberOfCuts = numRotations * minSidesPerRotation; + double cutSize = sourceAabb.XSize / numberOfCuts; + double cutPosition = sourceAabb.MinXYZ.X + cutSize; + var cuts = new List(); + for (int i = 0; i < numberOfCuts; i++) + { + cuts.Add(cutPosition); + cutPosition += cutSize; + } - var rotationCenter = new Vector3(sourceAabb.MinXYZ.X + (sourceAabb.MaxXYZ.X - sourceAabb.MinXYZ.X) * (startPercent / 100), - BendDirection == BendDirections.Bend_Up ? sourceAabb.MaxXYZ.Y + radius : sourceAabb.MinXYZ.Y - radius, - sourceAabb.Center.Z); + var rotationCenter = new Vector3(sourceAabb.MinXYZ.X + (sourceAabb.MaxXYZ.X - sourceAabb.MinXYZ.X) * (startPercent / 100), + BendDirection == BendDirections.Bend_Up ? sourceAabb.MaxXYZ.Y + radius : sourceAabb.MinXYZ.Y - radius, + sourceAabb.Center.Z); - var curvedChildren = new List(); + var curvedChildren = new List(); - var status = new ProgressStatus(); + foreach (var sourceItem in SourceContainer.VisibleMeshes()) + { + var originalMesh = sourceItem.Mesh; + reporter?.Invoke(0, "Copy Mesh".Localize()); + var transformedMesh = originalMesh.Copy(CancellationToken.None); + var itemMatrix = sourceItem.WorldMatrix(SourceContainer); - foreach (var sourceItem in SourceContainer.VisibleMeshes()) - { - var originalMesh = sourceItem.Mesh; - status.Status = "Copy Mesh".Localize(); - reporter.Report(status); - var transformedMesh = originalMesh.Copy(CancellationToken.None); - var itemMatrix = sourceItem.WorldMatrix(SourceContainer); + // transform into this space + transformedMesh.Transform(itemMatrix); - // transform into this space - transformedMesh.Transform(itemMatrix); + if (SplitMesh) + { + reporter?.Invoke(0, "Split Mesh".Localize()); - if (SplitMesh) - { - status.Status = "Split Mesh".Localize(); - reporter.Report(status); + // split the mesh along the x axis + transformedMesh.SplitOnPlanes(Vector3.UnitX, cuts, cutSize / 8); + } - // split the mesh along the x axis - transformedMesh.SplitOnPlanes(Vector3.UnitX, cuts, cutSize / 8); - } + for (int i = 0; i < transformedMesh.Vertices.Count; i++) + { + var position = transformedMesh.Vertices[i]; - for (int i = 0; i < transformedMesh.Vertices.Count; i++) - { - var position = transformedMesh.Vertices[i]; + var angleToRotate = ((position.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; + var distanceFromCenter = rotationCenter.Y - position.Y; + if (BendDirection == BendDirections.Bend_Down) + { + angleToRotate = -angleToRotate; + distanceFromCenter = -distanceFromCenter; + } - var angleToRotate = ((position.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; - var distanceFromCenter = rotationCenter.Y - position.Y; - if (BendDirection == BendDirections.Bend_Down) - { - angleToRotate = -angleToRotate; - distanceFromCenter = -distanceFromCenter; - } + var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; + rotatePosition.Z = position.Z; + transformedMesh.Vertices[i] = rotatePosition + new Vector3Float(rotationCenter.X, radius + sourceAabb.MaxXYZ.Y, 0); + } - var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; - rotatePosition.Z = position.Z; - transformedMesh.Vertices[i] = rotatePosition + new Vector3Float(rotationCenter.X, radius + sourceAabb.MaxXYZ.Y, 0); - } + // transform back into item local space + transformedMesh.Transform(Matrix4X4.CreateTranslation(-rotationCenter) * itemMatrix.Inverted); - // transform back into item local space - transformedMesh.Transform(Matrix4X4.CreateTranslation(-rotationCenter) * itemMatrix.Inverted); + if (SplitMesh) + { + reporter?.Invoke(0, "Merge Vertices".Localize()); - if (SplitMesh) - { - status.Status = "Merge Vertices".Localize(); - reporter.Report(status); + transformedMesh.MergeVertices(.1); + } - transformedMesh.MergeVertices(.1); - } + transformedMesh.CalculateNormals(); - transformedMesh.CalculateNormals(); + var curvedChild = new Object3D() + { + Mesh = transformedMesh + }; - var curvedChild = new Object3D() - { - Mesh = transformedMesh - }; + curvedChild.CopyWorldProperties(sourceItem, SourceContainer, Object3DPropertyFlags.All, false); - curvedChild.CopyWorldProperties(sourceItem, SourceContainer, Object3DPropertyFlags.All, false); + if (BendDirection == BendDirections.Bend_Down) + { + curvedChild.Translate(0, -sourceAabb.YSize - diameter, 0); + } - if (BendDirection == BendDirections.Bend_Down) - { - curvedChild.Translate(0, -sourceAabb.YSize - diameter, 0); - } + curvedChildren.Add(curvedChild); + } - curvedChildren.Add(curvedChild); - } + RemoveAllButSource(); + this.SourceContainer.Visible = false; - RemoveAllButSource(); - this.SourceContainer.Visible = false; + this.Children.Modify((list) => + { + list.AddRange(curvedChildren); + }); - this.Children.Modify((list) => - { - list.AddRange(curvedChildren); - }); + if (firstBuild) + { + var postAabb = this.GetAxisAlignedBoundingBox(); + PostCurveOffset = new Vector3(initialAabb.Center.X - postAabb.Center.X, + initialAabb.MinXYZ.Y - postAabb.MinXYZ.Y, + initialAabb.MinXYZ.Z - postAabb.MinXYZ.Z); + this.Matrix *= Matrix4X4.CreateTranslation(PostCurveOffset); + } - if (firstBuild) - { - var postAabb = this.GetAxisAlignedBoundingBox(); - PostCurveOffset = new Vector3(initialAabb.Center.X - postAabb.Center.X, - initialAabb.MinXYZ.Y - postAabb.MinXYZ.Y, - initialAabb.MinXYZ.Z - postAabb.MinXYZ.Z); - this.Matrix *= Matrix4X4.CreateTranslation(PostCurveOffset); - } + ApplyHoles(reporter, cancellationToken.Token); - ApplyHoles(reporter, cancellationToken.Token); + this.cancellationToken = null; + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + Invalidate(InvalidateType.DisplayValues); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - this.cancellationToken = null; - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - Invalidate(InvalidateType.DisplayValues); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + return Task.CompletedTask; + }); + } - return Task.CompletedTask; - }); - } + private Dictionary changeSet = new Dictionary(); - private Dictionary changeSet = new Dictionary(); - - public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(Diameter), () => BendType == BendTypes.Diameter); - change.SetRowVisible(nameof(Angle), () => BendType == BendTypes.Angle); - change.SetRowVisible(nameof(MinSidesPerRotation), () => SplitMesh); - } - } + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(Diameter), () => BendType == BendTypes.Diameter); + change.SetRowVisible(nameof(Angle), () => BendType == BendTypes.Angle); + change.SetRowVisible(nameof(MinSidesPerRotation), () => SplitMesh); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/DecimateObject3D.cs b/MatterControlLib/DesignTools/Operations/DecimateObject3D.cs index e3e2b25f9..9b5adbe83 100644 --- a/MatterControlLib/DesignTools/Operations/DecimateObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/DecimateObject3D.cs @@ -135,12 +135,12 @@ namespace MatterHackers.MatterControl.DesignTools // check if we have be initialized if (Mode == ReductionMode.Polygon_Count) { - TargetCount = agg_basics.Clamp(TargetCount, 4, SourcePolygonCount, ref valuesChanged); + TargetCount = Agg.Util.Clamp(TargetCount, 4, SourcePolygonCount, ref valuesChanged); TargetPercent = TargetCount / (double)SourcePolygonCount * 100; } else { - TargetPercent = agg_basics.Clamp(TargetPercent, 0, 100, ref valuesChanged); + TargetPercent = Agg.Util.Clamp(TargetPercent, 0, 100, ref valuesChanged); TargetCount = (int)(SourcePolygonCount * TargetPercent / 100); } diff --git a/MatterControlLib/DesignTools/Operations/FitToBoundsObject3D_3.cs b/MatterControlLib/DesignTools/Operations/FitToBoundsObject3D_3.cs index 9940db610..a3cce373d 100644 --- a/MatterControlLib/DesignTools/Operations/FitToBoundsObject3D_3.cs +++ b/MatterControlLib/DesignTools/Operations/FitToBoundsObject3D_3.cs @@ -169,7 +169,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/FitToCylinderObject3D.cs b/MatterControlLib/DesignTools/Operations/FitToCylinderObject3D.cs index 7fb3e92c7..1bbe0f827 100644 --- a/MatterControlLib/DesignTools/Operations/FitToCylinderObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/FitToCylinderObject3D.cs @@ -150,7 +150,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/Image/Histogram.cs b/MatterControlLib/DesignTools/Operations/Image/Histogram.cs index a51e97c10..bfedff2d3 100644 --- a/MatterControlLib/DesignTools/Operations/Image/Histogram.cs +++ b/MatterControlLib/DesignTools/Operations/Image/Histogram.cs @@ -419,7 +419,7 @@ namespace MatterHackers.MatterControl.DesignTools { var offset = e.Position.X - leftX; var newStart = RangeStart + offset / histogramBackground.Width; - newStart = agg_basics.Clamp(newStart, 0, RangeEnd); + newStart = Util.Clamp(newStart, 0, RangeEnd); if (RangeStart != newStart) { RangeStart = newStart; @@ -459,7 +459,7 @@ namespace MatterHackers.MatterControl.DesignTools { var offset = e.Position.X - rightX; var newEnd = RangeEnd + offset / histogramBackground.Width; - newEnd = agg_basics.Clamp(newEnd, RangeStart, 1); + newEnd = Util.Clamp(newEnd, RangeStart, 1); if (RangeEnd != newEnd) { RangeEnd = newEnd; diff --git a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs index 8c569f3b5..a63b616cb 100644 --- a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -54,329 +54,326 @@ using Polygons = System.Collections.Generic.List new { value, index }) - .OrderByDescending(vi => vi.value) - .First().value; - var graphics2D2 = _histogramRawCache.NewGraphics2D(); - graphics2D2.Clear(Color.White); - for (int i = 0; i < 256; i++) - { - graphics2D2.Line(i, 0, i, Easing.Exponential.Out(counts[i] / max) * _histogramRawCache.Height, Color.Black); - } - } - } + double max = counts.Select((value, index) => new { value, index }) + .OrderByDescending(vi => vi.value) + .First().value; + var graphics2D2 = _histogramRawCache.NewGraphics2D(); + graphics2D2.Clear(Color.White); + for (int i = 0; i < 256; i++) + { + graphics2D2.Line(i, 0, i, Easing.Exponential.Out(counts[i] / max) * _histogramRawCache.Height, Color.Black); + } + } + } - if (_histogramDisplayCache == null) - { - _histogramDisplayCache = new ImageBuffer(_histogramRawCache); - } + if (_histogramDisplayCache == null) + { + _histogramDisplayCache = new ImageBuffer(_histogramRawCache); + } - UpdateHistogramDisplay(); + UpdateHistogramDisplay(); - return _histogramDisplayCache; - } + return _histogramDisplayCache; + } - set - { - } - } + set + { + } + } - private void UpdateHistogramDisplay() - { - if (_histogramRawCache != null - && _histogramDisplayCache != null) - { - var graphics2D = _histogramDisplayCache.NewGraphics2D(); - graphics2D.Clear(Color.Transparent); - _histogramDisplayCache.CopyFrom(_histogramRawCache); - var rangeStart = RangeStart.Value(this); - var rangeEnd = RangeEnd.Value(this); - graphics2D.FillRectangle(0, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.Red, 100)); - graphics2D.FillRectangle(rangeEnd * _histogramDisplayCache.Width, 0, 255, _histogramDisplayCache.Height, new Color(Color.Red, 100)); - graphics2D.Line(rangeStart * _histogramDisplayCache.Width, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200)); - graphics2D.Line(rangeEnd * _histogramDisplayCache.Width, 0, rangeEnd * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200)); - } - } + private void UpdateHistogramDisplay() + { + if (_histogramRawCache != null + && _histogramDisplayCache != null) + { + var graphics2D = _histogramDisplayCache.NewGraphics2D(); + graphics2D.Clear(Color.Transparent); + _histogramDisplayCache.CopyFrom(_histogramRawCache); + var rangeStart = RangeStart.Value(this); + var rangeEnd = RangeEnd.Value(this); + graphics2D.FillRectangle(0, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.Red, 100)); + graphics2D.FillRectangle(rangeEnd * _histogramDisplayCache.Width, 0, 255, _histogramDisplayCache.Height, new Color(Color.Red, 100)); + graphics2D.Line(rangeStart * _histogramDisplayCache.Width, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200)); + graphics2D.Line(rangeEnd * _histogramDisplayCache.Width, 0, rangeEnd * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200)); + } + } - [JsonIgnore] - private ImageBuffer Image => this.Descendants().FirstOrDefault()?.Image; + [JsonIgnore] + private ImageBuffer Image => this.Descendants().FirstOrDefault()?.Image; - [Slider(0, 1)] - public DoubleOrExpression RangeStart { get; set; } = .1; + [Slider(0, 1)] + public DoubleOrExpression RangeStart { get; set; } = .1; - [Slider(0, 1)] - public DoubleOrExpression RangeEnd { get; set; } = 1; + [Slider(0, 1)] + public DoubleOrExpression RangeEnd { get; set; } = 1; - private IThresholdFunction ThresholdFunction - { - get - { - switch (FeatureDetector) - { - case ThresholdFunctions.Silhouette: - return new SilhouetteThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); + private IThresholdFunction ThresholdFunction + { + get + { + switch (FeatureDetector) + { + case ThresholdFunctions.Silhouette: + return new SilhouetteThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); - case ThresholdFunctions.Intensity: - return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this)); + case ThresholdFunctions.Intensity: + return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this)); - case ThresholdFunctions.Alpha: - return new AlphaThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); + case ThresholdFunctions.Alpha: + return new AlphaThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); - case ThresholdFunctions.Hue: - return new HueThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); - } + case ThresholdFunctions.Hue: + return new HueThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this)); + } - return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this)); - } - } + return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this)); + } + } - public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) - { - object3DControlsLayer.AddControls(ControlTypes.Standard2D); - } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.Standard2D); + } - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - this.DrawPath(); - } + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + this.DrawPath(); + } - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return this.GetWorldspaceAabbOfDrawPath(); - } + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return this.GetWorldspaceAabbOfDrawPath(); + } - public override bool CanApply => true; + public override bool CanApply => true; - public override void Apply(UndoBuffer undoBuffer) - { - this.FlattenToPathObject(undoBuffer); - } + public override void Apply(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } - public void GenerateMarchingSquaresAndLines(Action progressReporter, ImageBuffer image, IThresholdFunction thresholdFunction) - { - if (image != null) - { - // Regenerate outline - var marchingSquaresData = new MarchingSquaresByte( - image, - thresholdFunction.ZeroColor, - thresholdFunction.Threshold, - 0); + public void GenerateMarchingSquaresAndLines(Action progressReporter, ImageBuffer image, IThresholdFunction thresholdFunction) + { + if (image != null) + { + // Regenerate outline + var marchingSquaresData = new MarchingSquaresByte( + image, + thresholdFunction.ZeroColor, + thresholdFunction.Threshold, + 0); - progressReporter?.Invoke(0, "Creating Outline"); + progressReporter?.Invoke(0, "Creating Outline"); - marchingSquaresData.CreateLineSegments(); - progressReporter?.Invoke(.1, null); + marchingSquaresData.CreateLineSegments(); + progressReporter?.Invoke(.1, null); - int pixelsToIntPointsScale = 1000; - var lineLoops = marchingSquaresData.CreateLineLoops(pixelsToIntPointsScale); + int pixelsToIntPointsScale = 1000; + var lineLoops = marchingSquaresData.CreateLineLoops(pixelsToIntPointsScale); - progressReporter?.Invoke(.15, null); + progressReporter?.Invoke(.15, null); - var min = new IntPoint(-10, -10); - var max = new IntPoint(10 + image.Width * pixelsToIntPointsScale, 10 + image.Height * pixelsToIntPointsScale); + var min = new IntPoint(-10, -10); + var max = new IntPoint(10 + image.Width * pixelsToIntPointsScale, 10 + image.Height * pixelsToIntPointsScale); - var boundingPoly = new Polygon(); - boundingPoly.Add(min); - boundingPoly.Add(new IntPoint(min.X, max.Y)); - boundingPoly.Add(max); - boundingPoly.Add(new IntPoint(max.X, min.Y)); + var boundingPoly = new Polygon(); + boundingPoly.Add(min); + boundingPoly.Add(new IntPoint(min.X, max.Y)); + boundingPoly.Add(max); + boundingPoly.Add(new IntPoint(max.X, min.Y)); - // now clip the polygons to get the inside and outside polys - var clipper = new Clipper(); - clipper.AddPaths(lineLoops, PolyType.ptSubject, true); - clipper.AddPath(boundingPoly, PolyType.ptClip, true); + // now clip the polygons to get the inside and outside polys + var clipper = new Clipper(); + clipper.AddPaths(lineLoops, PolyType.ptSubject, true); + clipper.AddPath(boundingPoly, PolyType.ptClip, true); - var polygonShape = new Polygons(); - progressReporter?.Invoke(.3, null); + var polygonShape = new Polygons(); + progressReporter?.Invoke(.3, null); - clipper.Execute(ClipType.ctIntersection, polygonShape); + clipper.Execute(ClipType.ctIntersection, polygonShape); - progressReporter?.Invoke(.55, null); + progressReporter?.Invoke(.55, null); - polygonShape = Clipper.CleanPolygons(polygonShape, 100); + polygonShape = Clipper.CleanPolygons(polygonShape, 100); - progressReporter?.Invoke(.75, null); + progressReporter?.Invoke(.75, null); - VertexStorage rawVectorShape = polygonShape.PolygonToPathStorage(); + VertexStorage rawVectorShape = polygonShape.PolygonToPathStorage(); - var aabb = this.VisibleMeshes().FirstOrDefault().GetAxisAlignedBoundingBox(); - var xScale = aabb.XSize / image.Width; + var aabb = this.VisibleMeshes().FirstOrDefault().GetAxisAlignedBoundingBox(); + var xScale = aabb.XSize / image.Width; - var affine = Affine.NewScaling(1.0 / pixelsToIntPointsScale * xScale); - affine *= Affine.NewTranslation(-aabb.XSize / 2, -aabb.YSize / 2); + var affine = Affine.NewScaling(1.0 / pixelsToIntPointsScale * xScale); + affine *= Affine.NewTranslation(-aabb.XSize / 2, -aabb.YSize / 2); - rawVectorShape.transform(affine); - this.VertexStorage = rawVectorShape; + rawVectorShape.transform(affine); + this.VertexStorage = rawVectorShape; - progressReporter?.Invoke(1, null); - } - } + progressReporter?.Invoke(1, null); + } + } - public override async void OnInvalidate(InvalidateArgs invalidateArgs) - { - if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Image) - && invalidateArgs.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) - { - await Rebuild(); - } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) - { - await Rebuild(); - } + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Image) + && invalidateArgs.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) + { + await Rebuild(); + } + else if (Expressions.NeedRebuild(this, invalidateArgs)) + { + await Rebuild(); + } - base.OnInvalidate(invalidateArgs); - } + base.OnInvalidate(invalidateArgs); + } - private Color GetRGBA(byte[] buffer, int offset) - { - return new Color(buffer[offset + 2], buffer[offset + 1], buffer[offset + 0], buffer[offset + 3]); - } + private Color GetRGBA(byte[] buffer, int offset) + { + return new Color(buffer[offset + 2], buffer[offset + 1], buffer[offset + 0], buffer[offset + 3]); + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - UpdateHistogramDisplay(); - bool propertyUpdated = false; - var minSeparation = .01; - var rangeStart = RangeStart.Value(this); - var rangeEnd = RangeEnd.Value(this); - if (rangeStart < 0 - || rangeStart > 1 - || rangeEnd < 0 - || rangeEnd > 1 - || rangeStart > rangeEnd - minSeparation) - { - rangeStart = Math.Max(0, Math.Min(1 - minSeparation, rangeStart)); - rangeEnd = Math.Max(0, Math.Min(1, rangeEnd)); - if (rangeStart > rangeEnd - minSeparation) - { - // values are overlapped or too close together - if (rangeEnd < 1 - minSeparation) - { - // move the end up whenever possible - rangeEnd = rangeStart + minSeparation; - } - else - { - // move the end to the end and the start up - rangeEnd = 1; - RangeStart = 1 - minSeparation; - } - } + UpdateHistogramDisplay(); + bool propertyUpdated = false; + var minSeparation = .01; + var rangeStart = RangeStart.Value(this); + var rangeEnd = RangeEnd.Value(this); + if (rangeStart < 0 + || rangeStart > 1 + || rangeEnd < 0 + || rangeEnd > 1 + || rangeStart > rangeEnd - minSeparation) + { + rangeStart = Math.Max(0, Math.Min(1 - minSeparation, rangeStart)); + rangeEnd = Math.Max(0, Math.Min(1, rangeEnd)); + if (rangeStart > rangeEnd - minSeparation) + { + // values are overlapped or too close together + if (rangeEnd < 1 - minSeparation) + { + // move the end up whenever possible + rangeEnd = rangeStart + minSeparation; + } + else + { + // move the end to the end and the start up + rangeEnd = 1; + RangeStart = 1 - minSeparation; + } + } - propertyUpdated = true; - } + propertyUpdated = true; + } - var rebuildLock = RebuildLock(); - // now create a long running task to process the image - return ApplicationController.Instance.Tasks.Execute( - "Calculate Path".Localize(), - null, - (reporter, cancellationToken) => - { - var progressStatus = new ProgressStatus(); - this.GenerateMarchingSquaresAndLines( - (progress0to1, status) => - { - progressStatus.Progress0To1 = progress0to1; - progressStatus.Status = status; - reporter.Report(progressStatus); - }, - Image, - ThresholdFunction); + var rebuildLock = RebuildLock(); + // now create a long running task to process the image + return ApplicationController.Instance.Tasks.Execute( + "Calculate Path".Localize(), + null, + (reporter, cancellationToken) => + { + this.GenerateMarchingSquaresAndLines( + (progress0to1, status) => + { + reporter?.Invoke(progress0to1, status); + }, + Image, + ThresholdFunction); - if (propertyUpdated) - { - UpdateHistogramDisplay(); - this.Invalidate(InvalidateType.Properties); - } + if (propertyUpdated) + { + UpdateHistogramDisplay(); + this.Invalidate(InvalidateType.Properties); + } - UiThread.RunOnIdle(() => - { - rebuildLock.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path)); - }); + UiThread.RunOnIdle(() => + { + rebuildLock.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path)); + }); - return Task.CompletedTask; - }); - } - } + return Task.CompletedTask; + }); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D_2.cs b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D_2.cs index bb61b6843..90ff4511c 100644 --- a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D_2.cs +++ b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D_2.cs @@ -357,7 +357,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { CopyNewImageData(); await Rebuild(); @@ -391,16 +391,13 @@ namespace MatterHackers.MatterControl.DesignTools null, (reporter, cancellationToken) => { - var progressStatus = new ProgressStatus(); switch (AnalysisType) { case AnalysisTypes.Transparency: this.GenerateMarchingSquaresAndLines( (progress0to1, status) => { - progressStatus.Progress0To1 = progress0to1; - progressStatus.Status = status; - reporter.Report(progressStatus); + reporter?.Invoke(progress0to1, status); }, SourceImage, new AlphaFunction()); @@ -411,10 +408,8 @@ namespace MatterHackers.MatterControl.DesignTools this.GenerateMarchingSquaresAndLines( (progress0to1, status) => { - progressStatus.Progress0To1 = progress0to1; - progressStatus.Status = status; - reporter.Report(progressStatus); - }, + reporter?.Invoke(progress0to1, status); + }, alphaImage, new AlphaFunction()); break; diff --git a/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs b/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs index c4f39b49e..9749551ad 100644 --- a/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,19 +32,43 @@ using MatterHackers.DataConverters3D; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.VectorMath; +using System; +using System.Collections.Generic; namespace MatterHackers.MatterControl.DesignTools { - public class PathObject3D : Object3D, IEditorDraw - { - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - this.DrawPath(); - } + public class PathObject3D : Object3D, IEditorDraw, IPrimaryOperationsSpecifier + { + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + this.DrawPath(); + } - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return this.GetWorldspaceAabbOfDrawPath(); - } - } + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return this.GetWorldspaceAabbOfDrawPath(); + } + + public static IEnumerable GetOperations(Type type) + { + // path Ids + var pathIds = new List(new string[] { + "LinearExtrude", + "Revolve", + "InflatePath", + "OutlinePath", + "SmoothPath" + }); + + foreach (var pathId in pathIds) + { + yield return SceneOperations.ById(pathId); + } + } + + public IEnumerable GetOperations() + { + return GetOperations(this.GetType()); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/Path/InflatePathObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/InflatePathObject3D.cs index b1993c07d..0714c2761 100644 --- a/MatterControlLib/DesignTools/Operations/Path/InflatePathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/InflatePathObject3D.cs @@ -82,7 +82,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs index 494593f65..a9e151d5d 100644 --- a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,9 +32,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; -using CsvHelper; using MatterHackers.Agg.UI; -using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.Localizations; @@ -42,26 +40,25 @@ using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.PolygonMesh; using MatterHackers.PolygonMesh.Processors; using MatterHackers.VectorMath; -using static g3.SVGWriter; namespace MatterHackers.MatterControl.DesignTools.Operations { - public class LinearExtrudeObject3D : Object3D + public class LinearExtrudeObject3D : Object3D, IPrimaryOperationsSpecifier #if DEBUG , IPropertyGridModifier #endif - { - [Description("The height of the extrusion")] - [Slider(.1, 50, Easing.EaseType.Quadratic, useSnappingGrid: true)] - [MaxDecimalPlaces(2)] - public DoubleOrExpression Height { get; set; } = 5; + { + [Description("The height of the extrusion")] + [Slider(.1, 50, Easing.EaseType.Quadratic, useSnappingGrid: true)] + [MaxDecimalPlaces(2)] + public DoubleOrExpression Height { get; set; } = 5; #if DEBUG - [Description("Bevel the top of the extrusion")] - public bool BevelTop { get; set; } = false; + [Description("Bevel the top of the extrusion")] + public bool BevelTop { get; set; } = false; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ExpandStyles Style { get; set; } = ExpandStyles.Sharp; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ExpandStyles Style { get; set; } = ExpandStyles.Sharp; [Slider(0, 20, Easing.EaseType.Quadratic, snapDistance: .1)] public DoubleOrExpression Radius { get; set; } = 3; @@ -72,141 +69,140 @@ namespace MatterHackers.MatterControl.DesignTools.Operations public override bool CanApply => true; - public override IVertexSource GetVertexSource() - { - return this.CombinedVisibleChildrenPaths(); - } + public override void Apply(UndoBuffer undoBuffer) + { + if (Mesh == null) + { + Cancel(undoBuffer); + } + else + { + // only keep the mesh and get rid of everything else + using (RebuildLock()) + { + var meshOnlyItem = new Object3D() + { + Mesh = this.Mesh.Copy(CancellationToken.None) + }; - public override void Apply(UndoBuffer undoBuffer) - { - if (Mesh == null) - { - Cancel(undoBuffer); - } - else - { - // only keep the mesh and get rid of everything else - using (RebuildLock()) - { - var meshOnlyItem = new Object3D() - { - Mesh = this.Mesh.Copy(CancellationToken.None) - }; + meshOnlyItem.CopyProperties(this, Object3DPropertyFlags.All); - meshOnlyItem.CopyProperties(this, Object3DPropertyFlags.All); + // and replace us with the children + undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { meshOnlyItem })); + } - // and replace us with the children - undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { meshOnlyItem })); - } + Invalidate(InvalidateType.Children); + } + } - Invalidate(InvalidateType.Children); - } - } + public LinearExtrudeObject3D() + { + Name = "Linear Extrude".Localize(); + } - public LinearExtrudeObject3D() - { - Name = "Linear Extrude".Localize(); - } - - public override async void OnInvalidate(InvalidateArgs invalidateArgs) - { - if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Path) - || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children)) - && invalidateArgs.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateArgs.Source == this) - { - await Rebuild(); - } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Path) + || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children)) + && invalidateArgs.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateArgs.Source == this) + { + await Rebuild(); + } + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } else { - base.OnInvalidate(invalidateArgs); - } - } + base.OnInvalidate(invalidateArgs); + } + } private (double x, double y) GetOffset(double radius, double xRatio, double yRatio) - { + { return (radius * Math.Cos(xRatio * MathHelper.Tau / 4), radius * Math.Sin(yRatio * MathHelper.Tau / 4)); } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); - var rebuildLock = RebuildLock(); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + var rebuildLock = RebuildLock(); - bool valuesChanged = false; + bool valuesChanged = false; - var height = Height.Value(this); + var height = Height.Value(this); #if DEBUG - var segments = Segments.ClampIfNotCalculated(this, 1, 32, ref valuesChanged); + var segments = Segments.ClampIfNotCalculated(this, 1, 32, ref valuesChanged); var aabb = this.GetAxisAlignedBoundingBox(); var radius = Radius.ClampIfNotCalculated(this, 0, Math.Min(aabb.XSize, Math.Min(aabb.YSize, aabb.ZSize)) / 2, ref valuesChanged); var bevelStart = height - radius; #endif - // now create a long running task to do the extrusion - return ApplicationController.Instance.Tasks.Execute( - "Linear Extrude".Localize(), - null, - (reporter, cancellationToken) => - { - var vertexSource = this.GetVertexSource(); - List<(double height, double inset)> bevel = null; + // now create a long running task to do the extrusion + return ApplicationController.Instance.Tasks.Execute( + "Linear Extrude".Localize(), + null, + (reporter, cancellationToken) => + { + var childPaths = this.CombinedVisibleChildrenPaths(); + List<(double height, double inset)> bevel = null; #if DEBUG - if (BevelTop) - { + if (BevelTop) + { bevel = new List<(double, double)>(); for (int i = 0; i < segments; i++) - { + { (double x, double y) = GetOffset(radius, (i + 1) / (double)segments, i / (double)segments); - bevel.Add((bevelStart + y, -radius+x)); - } + bevel.Add((bevelStart + y, -radius + x)); + } } #endif - if (this.GetVertexSource() != null) - { + if (childPaths != null) + { #if DEBUG - Mesh = VertexSourceToMesh.Extrude(this.GetVertexSource(), height, bevel, InflatePathObject3D.GetJoinType(Style)); + Mesh = VertexSourceToMesh.Extrude(childPaths, height, bevel, InflatePathObject3D.GetJoinType(Style)); #else - Mesh = VertexSourceToMesh.Extrude(this.GetVertexSource(), height, bevel, ClipperLib.JoinType.jtRound); + Mesh = VertexSourceToMesh.Extrude(childPaths, height, bevel, ClipperLib.JoinType.jtRound); #endif if (Mesh.Vertices.Count == 0) - { - Mesh = null; - } - } + { + Mesh = null; + } + } else { - Mesh = null; + Mesh = null; } - UiThread.RunOnIdle(() => - { - rebuildLock.Dispose(); - Invalidate(InvalidateType.DisplayValues); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); - }); + UiThread.RunOnIdle(() => + { + rebuildLock.Dispose(); + Invalidate(InvalidateType.DisplayValues); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); + }); - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); + } #if DEBUG - public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(Radius), () => BevelTop); - change.SetRowVisible(nameof(Segments), () => BevelTop); - change.SetRowVisible(nameof(Style), () => BevelTop); - } + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(Radius), () => BevelTop); + change.SetRowVisible(nameof(Segments), () => BevelTop); + change.SetRowVisible(nameof(Style), () => BevelTop); + } #endif - } + public IEnumerable GetOperations() + { + yield return SceneOperations.ById("AddBase"); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs index 927925b26..2f5594bd7 100644 --- a/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,7 +32,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; @@ -43,121 +42,121 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.DesignTools.Operations { - public class MergePathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider - { - private ClipperLib.ClipType clipType; - private string operationName; + public class MergePathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider, IPrimaryOperationsSpecifier + { + private ClipperLib.ClipType clipType; + private string operationName; - public MergePathObject3D(string name, ClipperLib.ClipType clipType) - { - this.operationName = name; - this.clipType = clipType; - Name = name; - } + public MergePathObject3D(string name, ClipperLib.ClipType clipType) + { + this.operationName = name; + this.clipType = clipType; + Name = name; + } - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - this.DrawPath(); - } + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + this.DrawPath(); + } - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return this.GetWorldspaceAabbOfDrawPath(); - } + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return this.GetWorldspaceAabbOfDrawPath(); + } - public override bool CanApply => true; + public override bool CanApply => true; - public override void Apply(UndoBuffer undoBuffer) - { - this.FlattenToPathObject(undoBuffer); - } + public override void Apply(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } - public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) - { - object3DControlsLayer.AddControls(ControlTypes.Standard2D); - } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.Standard2D); + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); - return ApplicationController.Instance.Tasks.Execute( - operationName, - null, - (reporter, cancellationTokenSource) => - { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + return ApplicationController.Instance.Tasks.Execute( + operationName, + null, + (reporter, cancellationTokenSource) => + { + try + { + Merge(reporter, cancellationTokenSource.Token); + } + catch + { + } - try - { - Merge(reporter, cancellationTokenSource.Token); - } - catch - { - } + // set the mesh to show the path + this.Mesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight); - // set the mesh to show the path - this.Mesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight); + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); + return Task.CompletedTask; + }); + } - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); - return Task.CompletedTask; - }); - } + private void Merge(Action reporter, CancellationToken cancellationToken) + { + SourceContainer.Visible = true; + RemoveAllButSource(); - private void Merge(IProgress reporter, CancellationToken cancellationToken) - { - SourceContainer.Visible = true; - RemoveAllButSource(); + var participants = SourceContainer.VisiblePaths(); + if (participants.Count() < 2) + { + if (participants.Count() == 1) + { + var newMesh = new Object3D(); + newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All); + newMesh.Mesh = participants.First().Mesh; + this.Children.Add(newMesh); + SourceContainer.Visible = false; + } - var participants = SourceContainer.VisiblePaths(); - if (participants.Count() < 2) - { - if (participants.Count() == 1) - { - var newMesh = new Object3D(); - newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All); - newMesh.Mesh = participants.First().Mesh; - this.Children.Add(newMesh); - SourceContainer.Visible = false; - } + return; + } - return; - } + var first = participants.First(); + var resultsVertexSource = first.GetVertexSource().Transform(first.Matrix); - var first = participants.First(); - var resultsVertexSource = first.GetVertexSource().Transform(first.Matrix); + var totalOperations = participants.Count() - 1; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - var totalOperations = participants.Count() - 1; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + foreach (var item in participants) + { + if (item != first + && item.GetVertexSource() != null) + { + var itemVertexSource = item.GetVertexSource().Transform(item.Matrix); - var progressStatus = new ProgressStatus(); - foreach (var item in participants) - { - if (item != first - && item.GetVertexSource() != null) - { - var itemVertexSource = item.GetVertexSource().Transform(item.Matrix); + resultsVertexSource = resultsVertexSource.MergePaths(itemVertexSource, clipType); - resultsVertexSource = resultsVertexSource.MergePaths(itemVertexSource, clipType); + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, null); + } + } - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } - } + this.VertexStorage = new VertexStorage(resultsVertexSource); - this.VertexStorage = new VertexStorage(resultsVertexSource); + SourceContainer.Visible = false; + } - SourceContainer.Visible = false; - } - } + public IEnumerable GetOperations() + { + return PathObject3D.GetOperations(this.GetType()); + } + } } diff --git a/MatterControlLib/DesignTools/Operations/Path/OutlinePathObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/OutlinePathObject3D.cs index 7d6bb8cad..61344a27b 100644 --- a/MatterControlLib/DesignTools/Operations/Path/OutlinePathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/OutlinePathObject3D.cs @@ -78,7 +78,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/Path/RevolveObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/RevolveObject3D.cs index 55157ca8d..4b7fe7167 100644 --- a/MatterControlLib/DesignTools/Operations/Path/RevolveObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/RevolveObject3D.cs @@ -175,11 +175,11 @@ namespace MatterHackers.MatterControl.DesignTools.Operations if (startingAngle > 0 || endingAngle < 360) { - Sides = agg_basics.Clamp(sides, 1, 360, ref valuesChanged); + Sides = Util.Clamp(sides, 1, 360, ref valuesChanged); } else { - Sides = agg_basics.Clamp(sides, 3, 360, ref valuesChanged); + Sides = Util.Clamp(sides, 3, 360, ref valuesChanged); } Invalidate(InvalidateType.DisplayValues); diff --git a/MatterControlLib/DesignTools/Operations/Path/SmoothPathObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/SmoothPathObject3D.cs index 92bc1a004..7f496d5e6 100644 --- a/MatterControlLib/DesignTools/Operations/Path/SmoothPathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/SmoothPathObject3D.cs @@ -78,7 +78,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/RotateObject3D_2.cs b/MatterControlLib/DesignTools/Operations/RotateObject3D_2.cs index 3290cdab1..8a49cdd67 100644 --- a/MatterControlLib/DesignTools/Operations/RotateObject3D_2.cs +++ b/MatterControlLib/DesignTools/Operations/RotateObject3D_2.cs @@ -142,7 +142,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/TranslateObject3D.cs b/MatterControlLib/DesignTools/Operations/TranslateObject3D.cs index b98b16885..6af16e991 100644 --- a/MatterControlLib/DesignTools/Operations/TranslateObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/TranslateObject3D.cs @@ -86,7 +86,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Operations/TwistObject3D.cs b/MatterControlLib/DesignTools/Operations/TwistObject3D.cs index f518af265..17c667970 100644 --- a/MatterControlLib/DesignTools/Operations/TwistObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/TwistObject3D.cs @@ -226,21 +226,19 @@ namespace MatterHackers.MatterControl.DesignTools var twistedChildren = new List(); - var status = new ProgressStatus(); - foreach (var sourceItem in SourceContainer.VisibleMeshes()) { var originalMesh = sourceItem.Mesh; - status.Status = "Copy Mesh".Localize(); - reporter.Report(status); + var status = "Copy Mesh".Localize(); + reporter?.Invoke(0, status); var transformedMesh = originalMesh.Copy(CancellationToken.None); var itemMatrix = sourceItem.WorldMatrix(SourceContainer); // transform into this space transformedMesh.Transform(itemMatrix); - status.Status = "Split Mesh".Localize(); - reporter.Report(status); + status = "Split Mesh".Localize(); + reporter?.Invoke(0, status); // split the mesh along the z axis transformedMesh.SplitOnPlanes(Vector3.UnitZ, cuts, cutSize / 8); diff --git a/MatterControlLib/DesignTools/Primitives/BaseObject3D.cs b/MatterControlLib/DesignTools/Primitives/BaseObject3D.cs index f6e02974c..e31909720 100644 --- a/MatterControlLib/DesignTools/Primitives/BaseObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/BaseObject3D.cs @@ -194,7 +194,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs b/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs index 8486eee38..8a86378a9 100644 --- a/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2019, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,101 +27,119 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ +using System; using System.Threading.Tasks; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; -using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.PolygonMesh.Processors; using MatterHackers.VectorMath; -namespace MatterHackers.MatterControl.DesignTools +namespace MatterHackers.MatterControl.DesignTools.Primitives { - public class BoxPathObject3D : PrimitiveObject3D, IObject3DControlsProvider, IEditorDraw - { - public BoxPathObject3D() - { - Name = "Box".Localize(); - Color = Operations.Object3DExtensions.PrimitiveColors["Cube"]; - } + public class BoxPathObject3D : PathObject3D, IObject3DControlsProvider, IEditorDraw, IPropertyGridModifier, IStaticThumbnail + { + public BoxPathObject3D() + { + Name = "Box".Localize(); + Color = MatterHackers.MatterControl.DesignTools.Operations.Object3DExtensions.PrimitiveColors["Cube"]; + } - public override string ThumbnailName => "Box"; + public static double MinEdgeSize = .001; - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - this.DrawPath(); - } + public string ThumbnailName => "Box"; - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return this.GetWorldspaceAabbOfDrawPath(); - } + /// + /// This is the actual serialized with that can use expressions + /// + [MaxDecimalPlaces(2)] + [Slider(1, 400, Easing.EaseType.Quadratic, useSnappingGrid: true)] + public DoubleOrExpression Width { get; set; } = 20; - /// - /// This is the actual serialized with that can use expressions - /// - [MaxDecimalPlaces(2)] - [Slider(1, 400, Easing.EaseType.Quadratic, snapDistance: 1)] - public DoubleOrExpression Width { get; set; } = 20; + [MaxDecimalPlaces(2)] + [Slider(1, 400, Easing.EaseType.Quadratic, useSnappingGrid: true)] + public DoubleOrExpression Depth { get; set; } = 20; - [MaxDecimalPlaces(2)] - [Slider(1, 400, Easing.EaseType.Quadratic, snapDistance: 1)] - public DoubleOrExpression Depth { get; set; } = 20; + public bool Round { get; set; } - public static async Task Create() - { - var item = new BoxPathObject3D(); - await item.Rebuild(); - return item; - } + [Slider(0, 20, Easing.EaseType.Quadratic, snapDistance: .1)] + public DoubleOrExpression Radius { get; set; } = 3; - public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) - { - object3DControlsLayer.AddControls(ControlTypes.MoveInZ); - object3DControlsLayer.AddWidthDepthControls(this, Width, Depth, null); + [Slider(1, 30, Easing.EaseType.Quadratic, snapDistance: 1)] + public IntOrExpression RoundSegments { get; set; } = 9; - object3DControlsLayer.AddControls(ControlTypes.MoveInZ); - object3DControlsLayer.AddControls(ControlTypes.RotateXYZ); - } + public static async Task Create() + { + var item = new BoxPathObject3D(); + await item.Rebuild(); + return item; + } - public override async void OnInvalidate(InvalidateArgs invalidateArgs) - { - if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) - { - await Rebuild(); - } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateArgs); - } - } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.MoveInZ); + object3DControlsLayer.AddWidthDepthControls(this, Width, Depth, null); - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + object3DControlsLayer.AddControls(ControlTypes.MoveInZ); + object3DControlsLayer.AddControls(ControlTypes.RotateXYZ); + } - using (RebuildLock()) - { - using (new CenterAndHeightMaintainer(this)) - { - bool valuesChanged = false; - var width = Width.ClampIfNotCalculated(this, .01, 10000, ref valuesChanged); - var depth = Depth.ClampIfNotCalculated(this, .01, 10000, ref valuesChanged); - VertexStorage = new VertexStorage(new RoundedRect(-width / 2, -depth / 2, width / 2, depth / 2, 0)); + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this) + { + await Rebuild(); + } + else if (Expressions.NeedRebuild(this, invalidateArgs)) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateArgs); + } + } - this.Mesh = VertexStorage.Extrude(Constants.PathPolygonsHeight); - } - } + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path)); - return Task.CompletedTask; - } - } + using (RebuildLock()) + { + using (new CenterAndHeightMaintainer(this)) + { + bool valuesChanged = false; + var width = Width.ClampIfNotCalculated(this, MinEdgeSize, 1000000, ref valuesChanged); + var depth = Depth.ClampIfNotCalculated(this, MinEdgeSize, 1000000, ref valuesChanged); + var roundSegments = RoundSegments.ClampIfNotCalculated(this, 1, 90, ref valuesChanged); + var roundRadius = Radius.ClampIfNotCalculated(this, 0, Math.Min(width, depth) / 2, ref valuesChanged); + + if (Round) + { + var roundRect = new RoundedRect(-width / 2, -depth / 2, width / 2, depth / 2, roundRadius); + roundRect.NumSegments = roundSegments; + VertexStorage = new VertexStorage(roundRect); + } + else + { + VertexStorage = new VertexStorage(new RoundedRect(-width / 2, -depth / 2, width / 2, depth / 2, 0)); + } + + Mesh = VertexStorage.Extrude(Constants.PathPolygonsHeight); + } + } + + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path)); + return Task.CompletedTask; + } + + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(RoundSegments), () => Round); + change.SetRowVisible(nameof(Radius), () => Round); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Primitives/ConeObject3D.cs b/MatterControlLib/DesignTools/Primitives/ConeObject3D.cs index 73fafcafc..23dbb344d 100644 --- a/MatterControlLib/DesignTools/Primitives/ConeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/ConeObject3D.cs @@ -75,7 +75,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs b/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs index 90ce5877b..7cd25d163 100644 --- a/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs @@ -54,15 +54,15 @@ namespace MatterHackers.MatterControl.DesignTools /// This is the actual serialized with that can use expressions /// [MaxDecimalPlaces(2)] - [Slider(1, 400, VectorMath.Easing.EaseType.Quadratic, useSnappingGrid: true)] + [Slider(1, 400, Easing.EaseType.Quadratic, useSnappingGrid: true)] public DoubleOrExpression Width { get; set; } = 20; [MaxDecimalPlaces(2)] - [Slider(1, 400, VectorMath.Easing.EaseType.Quadratic, useSnappingGrid: true)] + [Slider(1, 400, Easing.EaseType.Quadratic, useSnappingGrid: true)] public DoubleOrExpression Depth { get; set; } = 20; [MaxDecimalPlaces(2)] - [Slider(1, 400, VectorMath.Easing.EaseType.Quadratic, useSnappingGrid: true)] + [Slider(1, 400, Easing.EaseType.Quadratic, useSnappingGrid: true)] public DoubleOrExpression Height { get; set; } = 20; public bool Round { get; set; } @@ -108,7 +108,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/CylinderObject3D.cs b/MatterControlLib/DesignTools/Primitives/CylinderObject3D.cs index a2f75200c..f97786451 100644 --- a/MatterControlLib/DesignTools/Primitives/CylinderObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/CylinderObject3D.cs @@ -156,7 +156,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/DescriptionObject3D.cs b/MatterControlLib/DesignTools/Primitives/DescriptionObject3D.cs index 28bf7a3b1..ef8155b34 100644 --- a/MatterControlLib/DesignTools/Primitives/DescriptionObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/DescriptionObject3D.cs @@ -201,7 +201,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/DualContouringObject3D.cs b/MatterControlLib/DesignTools/Primitives/DualContouringObject3D.cs index bfa2265b1..09ed62f5b 100644 --- a/MatterControlLib/DesignTools/Primitives/DualContouringObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/DualContouringObject3D.cs @@ -88,7 +88,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/HalfCylinderObject3D.cs b/MatterControlLib/DesignTools/Primitives/HalfCylinderObject3D.cs index 6b93c793a..8ed3d5ceb 100644 --- a/MatterControlLib/DesignTools/Primitives/HalfCylinderObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/HalfCylinderObject3D.cs @@ -75,7 +75,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/HalfSphereObject3D.cs b/MatterControlLib/DesignTools/Primitives/HalfSphereObject3D.cs index 952f045db..5d645d4e2 100644 --- a/MatterControlLib/DesignTools/Primitives/HalfSphereObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/HalfSphereObject3D.cs @@ -86,7 +86,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/HalfWedgeObject3D.cs b/MatterControlLib/DesignTools/Primitives/HalfWedgeObject3D.cs index 240cb9bb5..ce0faec35 100644 --- a/MatterControlLib/DesignTools/Primitives/HalfWedgeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/HalfWedgeObject3D.cs @@ -74,7 +74,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs index 430143063..254cf7a32 100644 --- a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs @@ -45,7 +45,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { public class OperationSourceContainerObject3D : Object3D { - public static Func, CancellationTokenSource, Task>, Task> TaskBuilder { get; set; } = + public static Func, CancellationTokenSource, Task>, Task> TaskBuilder { get; set; } = (name, func) => Task.Run(() => func(null, null)); public override Mesh Mesh @@ -207,7 +207,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } @@ -329,7 +329,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations /// Show progress /// Can check if the operation has been canceled /// Did any holes get subtracted - public bool ApplyHoles(IProgress reporter, + public bool ApplyHoles(Action reporter, CancellationToken cancellationToken) { var removeItems = Children.Where(c => c.WorldOutputType(SourceContainer) == PrintOutputTypes.Hole && c.Visible); diff --git a/MatterControlLib/DesignTools/Primitives/PyramidObject3D.cs b/MatterControlLib/DesignTools/Primitives/PyramidObject3D.cs index d14b5fd7c..3f88dcc57 100644 --- a/MatterControlLib/DesignTools/Primitives/PyramidObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/PyramidObject3D.cs @@ -75,7 +75,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs index fdf07f696..365d6877f 100644 --- a/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs @@ -180,7 +180,7 @@ namespace MatterHackers.MatterControl.DesignTools RebuildImage(); await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { RebuildImage(); await Rebuild(); diff --git a/MatterControlLib/DesignTools/Primitives/RingObject3D.cs b/MatterControlLib/DesignTools/Primitives/RingObject3D.cs index a5bb27981..922ad6954 100644 --- a/MatterControlLib/DesignTools/Primitives/RingObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/RingObject3D.cs @@ -121,7 +121,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/SetTemperatureObject3D.cs b/MatterControlLib/DesignTools/Primitives/SetTemperatureObject3D.cs index 436bed2ca..72e99b738 100644 --- a/MatterControlLib/DesignTools/Primitives/SetTemperatureObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/SetTemperatureObject3D.cs @@ -94,7 +94,7 @@ namespace MatterHackers.MatterControl.DesignTools using (RebuildLock()) { - Temperature = agg_basics.Clamp(Temperature, 140, 400, ref valuesChanged); + Temperature = Util.Clamp(Temperature, 140, 400, ref valuesChanged); using (new CenterAndHeightMaintainer(this)) { diff --git a/MatterControlLib/DesignTools/Primitives/SphereObject3D.cs b/MatterControlLib/DesignTools/Primitives/SphereObject3D.cs index abde10a61..faf612837 100644 --- a/MatterControlLib/DesignTools/Primitives/SphereObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/SphereObject3D.cs @@ -106,7 +106,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/TemperatureTowerObject3D.cs b/MatterControlLib/DesignTools/Primitives/TemperatureTowerObject3D.cs index 086b4a18c..f93cfcd32 100644 --- a/MatterControlLib/DesignTools/Primitives/TemperatureTowerObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/TemperatureTowerObject3D.cs @@ -120,9 +120,9 @@ namespace MatterHackers.MatterControl.DesignTools using (RebuildLock()) { - MaxTemperature = agg_basics.Clamp(MaxTemperature, 140, 400, ref valuesChanged); - Sections = agg_basics.Clamp(Sections, 2, 20, ref valuesChanged); - ChangeAmount = agg_basics.Clamp(ChangeAmount, 1, 30, ref valuesChanged); + MaxTemperature = Util.Clamp(MaxTemperature, 140, 400, ref valuesChanged); + Sections = Util.Clamp(Sections, 2, 20, ref valuesChanged); + ChangeAmount = Util.Clamp(ChangeAmount, 1, 30, ref valuesChanged); using (new CenterAndHeightMaintainer(this)) { diff --git a/MatterControlLib/DesignTools/Primitives/TextObject3D.cs b/MatterControlLib/DesignTools/Primitives/TextObject3D.cs index e16953207..96e615c73 100644 --- a/MatterControlLib/DesignTools/Primitives/TextObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/TextObject3D.cs @@ -82,7 +82,7 @@ namespace MatterHackers.MatterControl.DesignTools { var item = new TextObject3D(); - if (!setTo2D) + if (setTo2D) { item.Output = OutputDimensions.Output2D; } @@ -166,7 +166,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/TorusObject3D.cs b/MatterControlLib/DesignTools/Primitives/TorusObject3D.cs index ada6161d2..d4f7fc46a 100644 --- a/MatterControlLib/DesignTools/Primitives/TorusObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/TorusObject3D.cs @@ -97,7 +97,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/WedgeObject3D.cs b/MatterControlLib/DesignTools/Primitives/WedgeObject3D.cs index df3ae3d5d..bb28bc991 100644 --- a/MatterControlLib/DesignTools/Primitives/WedgeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/WedgeObject3D.cs @@ -85,7 +85,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Primitives/WedgeObject3D_2.cs b/MatterControlLib/DesignTools/Primitives/WedgeObject3D_2.cs index 78dad5a95..6475856f2 100644 --- a/MatterControlLib/DesignTools/Primitives/WedgeObject3D_2.cs +++ b/MatterControlLib/DesignTools/Primitives/WedgeObject3D_2.cs @@ -88,7 +88,7 @@ namespace MatterHackers.MatterControl.DesignTools { await Rebuild(); } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + else if (Expressions.NeedRebuild(this, invalidateArgs)) { await Rebuild(); } diff --git a/MatterControlLib/DesignTools/Sheets/DoubleOrExpression.cs b/MatterControlLib/DesignTools/Sheets/DoubleOrExpression.cs index 8b1f8ddf4..aa914567b 100644 --- a/MatterControlLib/DesignTools/Sheets/DoubleOrExpression.cs +++ b/MatterControlLib/DesignTools/Sheets/DoubleOrExpression.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2022, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -35,85 +35,85 @@ using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.DesignTools { - [TypeConverter(typeof(DoubleOrExpression))] - public class DoubleOrExpression : DirectOrExpression - { - public double Value(IObject3D owner) - { - var value = SheetObject3D.EvaluateExpression(owner, Expression); - if (owner.RebuildLocked) + [TypeConverter(typeof(DoubleOrExpression))] + public class DoubleOrExpression : DirectOrExpression + { + public double Value(IObject3D owner) + { + var value = Expressions.EvaluateExpression(owner, Expression); + if (owner.RebuildLocked) { ExpressionValueAtLastRebuild = value.ToString(); } - return value; - } + return value; + } - public DoubleOrExpression(double value) - { - Expression = value.ToString(); - } + public DoubleOrExpression(double value) + { + Expression = value.ToString(); + } - public DoubleOrExpression(string expression) - { - Expression = expression; - } + public DoubleOrExpression(string expression) + { + Expression = expression; + } - public static implicit operator DoubleOrExpression(double value) - { - return new DoubleOrExpression(value); - } + public static implicit operator DoubleOrExpression(double value) + { + return new DoubleOrExpression(value); + } - public static implicit operator DoubleOrExpression(string expression) - { - return new DoubleOrExpression(expression); - } + public static implicit operator DoubleOrExpression(string expression) + { + return new DoubleOrExpression(expression); + } - /// - /// Evaluate the expression clap the result and return the clamped value. - /// If the expression as not an equation, modify it to be the clamped value. - /// - /// The Object to find the table relative to - /// The min value to clamp to - /// The max value to clamp to - /// Did the value actual get changed (clamped). - /// - public double ClampIfNotCalculated(IObject3D item, double min, double max, ref bool valuesChanged) - { - var value = agg_basics.Clamp(this.Value(item), min, max, ref valuesChanged); - if (!this.IsEquation) - { - // clamp the actual expression as it is not an equation - Expression = value.ToString(); - } + /// + /// Evaluate the expression clap the result and return the clamped value. + /// If the expression as not an equation, modify it to be the clamped value. + /// + /// The Object to find the table relative to + /// The min value to clamp to + /// The max value to clamp to + /// Did the value actual get changed (clamped). + /// + public double ClampIfNotCalculated(IObject3D item, double min, double max, ref bool valuesChanged) + { + var value = Util.Clamp(this.Value(item), min, max, ref valuesChanged); + if (!this.IsEquation) + { + // clamp the actual expression as it is not an equation + Expression = value.ToString(); + } - return value; - } + return value; + } - public double DefaultAndClampIfNotCalculated(IObject3D item, - double min, - double max, - string keyName, - double defaultValue, - ref bool changed) - { - var currentValue = this.Value(item); - if (!this.IsEquation) - { - double databaseValue = UserSettings.Instance.Fields.GetDouble(keyName, defaultValue); + public double DefaultAndClampIfNotCalculated(IObject3D item, + double min, + double max, + string keyName, + double defaultValue, + ref bool changed) + { + var currentValue = this.Value(item); + if (!this.IsEquation) + { + double databaseValue = UserSettings.Instance.Fields.GetDouble(keyName, defaultValue); - if (currentValue == 0) - { - currentValue = databaseValue; - changed = true; - } + if (currentValue == 0) + { + currentValue = databaseValue; + changed = true; + } - currentValue = agg_basics.Clamp(currentValue, min, max, ref changed); + currentValue = Util.Clamp(currentValue, min, max, ref changed); - Expression = currentValue.ToString(); - } + Expression = currentValue.ToString(); + } - return currentValue; - } - } + return currentValue; + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Sheets/IntOrExpression.cs b/MatterControlLib/DesignTools/Sheets/IntOrExpression.cs index d26c3d5f6..5274de187 100644 --- a/MatterControlLib/DesignTools/Sheets/IntOrExpression.cs +++ b/MatterControlLib/DesignTools/Sheets/IntOrExpression.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2022, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -33,70 +33,70 @@ using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.DesignTools { - [TypeConverter(typeof(IntOrExpression))] - public class IntOrExpression : DirectOrExpression - { - public int Value(IObject3D owner) - { - var rebuilding = owner.RebuildLocked; - var value = SheetObject3D.EvaluateExpression(owner, Expression); - if (rebuilding) - { - ExpressionValueAtLastRebuild = value.ToString(); - } + [TypeConverter(typeof(IntOrExpression))] + public class IntOrExpression : DirectOrExpression + { + public int Value(IObject3D owner) + { + var rebuilding = owner.RebuildLocked; + var value = Expressions.EvaluateExpression(owner, Expression); + if (rebuilding) + { + ExpressionValueAtLastRebuild = value.ToString(); + } - return value; - } + return value; + } - public IntOrExpression(int value) - { - Expression = value.ToString(); - } + public IntOrExpression(int value) + { + Expression = value.ToString(); + } - public IntOrExpression(double value) - { - Expression = ((int)value).ToString(); - } + public IntOrExpression(double value) + { + Expression = ((int)value).ToString(); + } - public IntOrExpression(string expression) - { - Expression = expression; - } + public IntOrExpression(string expression) + { + Expression = expression; + } - public static implicit operator IntOrExpression(int value) - { - return new IntOrExpression(value); - } + public static implicit operator IntOrExpression(int value) + { + return new IntOrExpression(value); + } - public static implicit operator IntOrExpression(double value) - { - return new IntOrExpression(value); - } + public static implicit operator IntOrExpression(double value) + { + return new IntOrExpression(value); + } - public static implicit operator IntOrExpression(string expression) - { - return new IntOrExpression(expression); - } + public static implicit operator IntOrExpression(string expression) + { + return new IntOrExpression(expression); + } - /// - /// Evaluate the expression clap the result and return the clamped value. - /// If the expression as not an equation, modify it to be the clamped value. - /// - /// The Object to find the table relative to - /// The min value to clamp to - /// The max value to clamp to - /// Did the value actual get changed (clamped). - /// - public int ClampIfNotCalculated(IObject3D item, int min, int max, ref bool valuesChanged) - { - var value = agg_basics.Clamp(this.Value(item), min, max, ref valuesChanged); - if (!this.IsEquation) - { - // clamp the actual expression as it is not an equation - Expression = value.ToString(); - } + /// + /// Evaluate the expression clap the result and return the clamped value. + /// If the expression as not an equation, modify it to be the clamped value. + /// + /// The Object to find the table relative to + /// The min value to clamp to + /// The max value to clamp to + /// Did the value actual get changed (clamped). + /// + public int ClampIfNotCalculated(IObject3D item, int min, int max, ref bool valuesChanged) + { + var value = Util.Clamp(this.Value(item), min, max, ref valuesChanged); + if (!this.IsEquation) + { + // clamp the actual expression as it is not an equation + Expression = value.ToString(); + } - return value; - } - } + return value; + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs index 7152e332c..8e2bb09fa 100644 --- a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs +++ b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2019, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -28,660 +28,129 @@ either expressed or implied, of the FreeBSD Project. */ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using MatterHackers.Agg; using MatterHackers.Agg.Platform; -using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.PolygonMesh; -using org.mariuszgromada.math.mxparser; namespace MatterHackers.MatterControl.DesignTools { [HideChildrenFromTreeView] - [HideMeterialAndColor] - [WebPageLink("Documentation", "Open", "https://www.matterhackers.com/support/mattercontrol-variable-support")] - [MarkDownDescription("[BETA] - Experimental support for variables and equations with a sheets like interface.")] - public class SheetObject3D : Object3D, IStaticThumbnail - { - private SheetData _sheetData; - public SheetData SheetData - { - get => _sheetData; - - set - { - if (_sheetData != value) - { - if (_sheetData != null) - { - _sheetData.Recalculated -= SendInvalidateToAll; - } - - _sheetData = value; - _sheetData.Recalculated += SendInvalidateToAll; - } - } - } - - public static async Task Create() - { - var item = new SheetObject3D - { - SheetData = new SheetData(5, 5) - }; - await item.Rebuild(); - return item; - } - - public string ThumbnailName => "Sheet"; - - - private static object loadLock = new object(); - private static IObject3D sheetObject; - - public override Mesh Mesh - { - get - { - if (this.Children.Count == 0) - { - lock (loadLock) - { - if (sheetObject == null) - { - sheetObject = MeshContentProvider.LoadMCX(StaticData.Instance.OpenStream(Path.Combine("Stls", "sheet_icon.mcx"))); - } - - this.Children.Modify((list) => - { - list.Clear(); - - list.Add(sheetObject.Clone()); - }); - } - } - - return null; - } - - set => base.Mesh = value; - } - - public SheetObject3D() - { - } - - public override bool Printable => false; - - public class UpdateItem - { - internal int depth; - internal IObject3D item; - internal RebuildLock rebuildLock; - - public override string ToString() - { - var state = rebuildLock == null ? "unlocked" : "locked"; - return $"{depth} {state} - {item}"; - } - } - - public static List SortAndLockUpdateItems(IObject3D root, Func includeObject, bool checkForExpression) - { - var requiredUpdateItems = new Dictionary(); - foreach (var child in root.Descendants()) - { - if (includeObject(child)) - { - var parent = child; - var depthToThis = 0; - while(parent.Parent != root) - { - depthToThis++; - parent = parent.Parent; - } - - AddItemsRequiringUpdateToDictionary(child, requiredUpdateItems, depthToThis, includeObject, checkForExpression); - } - } - - var updateItems = requiredUpdateItems.Values.ToList(); - // sort them - updateItems.Sort((a, b) => a.depth.CompareTo(b.depth)); - - // lock everything - foreach (var depthItem in updateItems) - { - depthItem.rebuildLock = depthItem.item.RebuildLock(); - } - - return updateItems; - } - - private void SendInvalidateToAll(object s, EventArgs e) - { - var updateItems = SortAndLockUpdateItems(this.Parent, (item) => - { - if (item == this || item.Parent == this) - { - // don't process this - return false; - } - else if (item.Parent is ArrayObject3D arrayObject3D - && arrayObject3D.SourceContainer != item) - { - // don't process the copied children of an array object - return false; - } - - return true; - }, true); - - SendInvalidateInRebuildOrder(updateItems, InvalidateType.SheetUpdated, this); - } - - public static RunningInterval SendInvalidateInRebuildOrder(List updateItems, - InvalidateType invalidateType, - IObject3D sender = null) - { - // and send the invalidate - RunningInterval runningInterval = null; - void RebuildWhenUnlocked() - { - var count = updateItems.Count; - if (count > 0) - { - // get the last item from the list - var lastIndex = count - 1; - var lastUpdateItem = updateItems[lastIndex]; - // we start with everything locked, so unlock the last layer and tell it to rebuild - if (lastUpdateItem.rebuildLock != null) - { - // release the lock and rebuild - // and ask it to update - var depthToBuild = lastUpdateItem.depth; - for (int i = 0; i < updateItems.Count; i++) - { - var updateItem = updateItems[i]; - if (updateItem.depth == lastUpdateItem.depth) - { - updateItem.rebuildLock.Dispose(); - updateItem.rebuildLock = null; - var updateSender = sender == null ? updateItem.item : sender; - updateItem.item.Invalidate(new InvalidateArgs(updateSender, invalidateType)); - } - } - } - else if (updateItems.Where(i => - { - return i.depth == lastUpdateItem.depth && i.item.RebuildLocked; - }).Any()) - { - // wait for the current rebuild to end (the one we requested above) - return; - } - else - { - // now that all the items at this level have rebuilt, remove them from out tracking - for (int i = updateItems.Count - 1; i >= 0; i--) - { - if (updateItems[i].depth == lastUpdateItem.depth) - { - updateItems.RemoveAt(i); - } - } - } - } - else - { - UiThread.ClearInterval(runningInterval); - } - } - - // rebuild depth first - runningInterval = UiThread.SetInterval(RebuildWhenUnlocked, .01); - - return runningInterval; - } - - private static readonly Type[] ExpressionTypes = - { - typeof(StringOrExpression), - typeof(DoubleOrExpression), - typeof(IntOrExpression), - - }; - - - public static IEnumerable GetExpressionPropreties(IObject3D item) - { - return item.GetType().GetProperties(OwnedPropertiesOnly) - .Where(pi => ExpressionTypes.Contains(pi.PropertyType) - && pi.GetGetMethod() != null - && pi.GetSetMethod() != null) - .Select(p => new EditableProperty(p, item)); - } - - //private static bool HasValuesThatWillChange(IObject3D item) - // { - // // enumerate public properties on child - // foreach (var property in GetExpressionPropreties(item)) - // { - // var propertyValue = property.Value; - - // if (propertyValue is IDirectOrExpression expression) - // { - // // return the value - // var currentValue = item.GetType().GetProperty(property.Name).GetValue(child, null).ToString(); - // var newValue = EvaluateExpression(item, propertyValue.ToString()).ToString(); - // inExpression = inExpression.Replace("[" + constant + "]", value); - // } - // } - //} - - private static void AddItemsRequiringUpdateToDictionary(IObject3D inItem, - Dictionary updatedItems, - int inDepth, - Func includeObject, - bool checkForExpression) - { - // process depth first - foreach(var child in inItem.Children) - { - AddItemsRequiringUpdateToDictionary(child, updatedItems, inDepth + 1, includeObject, checkForExpression); - } - - var depth2 = inDepth; - if (includeObject(inItem) - && (!checkForExpression || HasExpressionWithString(inItem, "=", true))) - { - var itemToAdd = inItem; - while (itemToAdd != null - && depth2 >= 0) - { - updatedItems[itemToAdd] = new UpdateItem() - { - depth = depth2, - item = itemToAdd - }; - depth2--; - itemToAdd = itemToAdd?.Parent; - } - } - } - - private static readonly Regex ConstantFinder = new Regex("(?<=\\[).+?(?=\\])", RegexOptions.CultureInvariant | RegexOptions.Compiled); - private static Random rand = new Random(); - - private static Dictionary> constants = new Dictionary>() - { - // length - ["cm"] = (owner) => 10, - ["m"] = (owner) => 1000, - ["inch"] = (owner) => 25.4, - ["ft"] = (owner) => 304.8, - // math constant - ["pi"] = (owner) => Math.PI, - ["tau"] = (owner) => Math.PI * 2, - ["e"] = (owner) => Math.E, - // functions - ["rand"] = (owner) => rand.NextDouble(), - // array function - ["index"] = (owner) => RetrieveArrayIndex(owner, 0), - ["index0"] = (owner) => RetrieveArrayIndex(owner, 0), - ["index1"] = (owner) => RetrieveArrayIndex(owner, 1), - ["index2"] = (owner) => RetrieveArrayIndex(owner, 2), - }; - - private static ArrayObject3D FindParentArray(IObject3D item, int wantLevel) - { - int foundLevel = 0; - // look through all the parents - foreach (var parent in item.Parents()) - { - // if it is a sheet - if (parent is ArrayObject3D arrayObject) - { - if (foundLevel == wantLevel) - { - return arrayObject; - } - - foundLevel++; - } - } - - return null; - } - - public static int RetrieveArrayIndex(IObject3D item, int level) - { - var arrayObject = FindParentArray(item, level); - - if (arrayObject != null) - { - int index = 0; - foreach (var child in arrayObject.Children) - { - if (!(child is OperationSourceObject3D)) - { - if (child.DescendantsAndSelf().Where(i => i == item).Any()) - { - return index; - } - - index++; - } - } - } - - return 0; - } - - private static string ReplaceConstantsWithValues(IObject3D owner, string stringWithConstants) - { - string Replace(string inputString, string setting) - { - if (constants.ContainsKey(setting)) - { - var value = constants[setting]; - - // braces then brackets replacement - inputString = inputString.Replace("[" + setting + "]", value(owner).ToString()); - } - - return inputString; - } - - MatchCollection matches = ConstantFinder.Matches(stringWithConstants); - - for (int i = 0; i < matches.Count; i++) - { - var replacementTerm = matches[i].Value; - stringWithConstants = Replace(stringWithConstants, replacementTerm); - } - - return stringWithConstants; - } - - private static string GetDisplayName(PropertyInfo prop) - { - var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); - return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase(); - } - - private static string SearchSiblingProperties(IObject3D owner, string inExpression) + [HideMeterialAndColor] + [WebPageLink("Documentation", "Open", "https://www.matterhackers.com/support/mattercontrol-variable-support")] + [MarkDownDescription("[BETA] - Experimental support for variables and equations with a sheets like interface.")] + public class SheetObject3D : Object3D, IStaticThumbnail, IVariableContainer + { + private SheetData _sheetData; + public SheetData SheetData { - var parent = owner.Parent; - if (parent != null) - { - var matches = ConstantFinder.Matches(inExpression); + get => _sheetData; - for (int i = 0; i < matches.Count; i++) - { - var constant = matches[i].Value; - // split inExpression on . - var splitExpression = constant.Split('.'); - if (splitExpression.Length == 2) - { - foreach (var child in parent.Children) - { - // skip if owner - if (child != owner) - { - var itemName = splitExpression[0]; - var propertyName = splitExpression[1]; - // if child has the same name as itemName - if (child.Name == itemName) - { - // enumerate public properties on child - foreach (var property in child.GetType().GetProperties()) - { - var displayName = GetDisplayName(property); - // if property name matches propertyName - if (displayName == propertyName) - { - // return the value - var expression = child.GetType().GetProperty(property.Name).GetValue(child, null).ToString(); - var value = SheetObject3D.EvaluateExpression(child, expression).ToString(); - inExpression = inExpression.Replace("[" + constant + "]", value); - } - } - } - } - } - } - } + set + { + if (_sheetData != value) + { + if (_sheetData != null) + { + _sheetData.Recalculated -= SendInvalidateToAll; + } + + _sheetData = value; + _sheetData.Recalculated += SendInvalidateToAll; + } } - - return inExpression; } - public static T EvaluateExpression(IObject3D owner, string inExpression) - { - var inputExpression = inExpression; - var printer = owner.ContainingPrinter(); - if (printer != null) - { - inputExpression = printer.Settings.ReplaceSettingsNamesWithValues(inputExpression, false); - } - - inputExpression = SearchSiblingProperties(owner, inputExpression); - - inputExpression = ReplaceConstantsWithValues(owner, inputExpression); - - // check if the expression is an equation (starts with "=") - if (inputExpression.Length > 0 && inputExpression[0] == '=') - { - // look through all the parents - var sheet = FindFirstSheet(owner); - if (sheet != null) - { - // try to manage the cell into the correct data type - string value = sheet.SheetData.EvaluateExpression(inputExpression); - return CastResult(value, inputExpression); - } - - // could not find a sheet, try to evaluate the expression directly - var evaluator = new Expression(inputExpression.Substring(1).ToLower()); - if (evaluator.checkSyntax()) - { - Debug.WriteLine(evaluator.getErrorMessage()); - } - - return CastResult(evaluator.calculate().ToString(), inputExpression); - } - else // not an equation so try to parse it directly - { - if (typeof(T) == typeof(string)) - { - return (T)(object)inputExpression; - } - - double.TryParse(inputExpression, out var result); - - if (typeof(T) == typeof(double)) - { - return (T)(object)result; - } - if (typeof(T) == typeof(int)) - { - return (T)(object)(int)Math.Round(result); - } - - return (T)(object)(int)0; - } - } - - /// - /// Find the sheet that the given item will reference - /// - /// The item to start the search from - /// - private static SheetObject3D FindFirstSheet(IObject3D item) - { - // look through all the parents - foreach (var parent in item.Parents()) - { - // then each child of any give parent - foreach (var sibling in parent.Children) - { - // if it is a sheet - if (sibling != item - && sibling is SheetObject3D sheet) - { - return sheet; - } - } - } - - return null; - } - - /// - /// Check if there are any references from the item to the sheet. - /// - /// The item to validate editable properties on - /// The sheet to check if this object references - /// - public static bool NeedsRebuild(IObject3D itemToCheck, InvalidateArgs invalidateArgs) - { - if (!invalidateArgs.InvalidateType.HasFlag(InvalidateType.SheetUpdated)) - { - return false; - } - - if (invalidateArgs.Source is SheetObject3D sheet) - { - // Check if the sheet is the first sheet parent of this item (if not it will not change it's data). - if (FindFirstSheet(itemToCheck) == sheet) - { - return HasExpressionWithString(itemToCheck, "=", true); - } - } - - return false; - } - - public static IEnumerable GetActiveExpressions(IObject3D item, string checkForString, bool startsWith) + public static async Task Create() { - foreach (var property in PublicPropertyEditor.GetEditablePropreties(item)) - { - var propertyValue = property.Value; + var item = new SheetObject3D + { + SheetData = new SheetData(5, 5) + }; + await item.Rebuild(); + return item; + } - if (propertyValue is DirectOrExpression directOrExpression) - { - if (startsWith) - { - if (directOrExpression.Expression.StartsWith(checkForString)) - { - // WIP: check if the value has actually changed, this will update every object on any cell change - yield return directOrExpression; - } - } - else - { - if(directOrExpression.Expression.Contains(checkForString)) - { - yield return directOrExpression; - } - } - } - } - } + public string ThumbnailName => "Sheet"; - public static IEnumerable GetComponentExpressions(ComponentObject3D component, string checkForString, bool startsWith) - { - for (var i = 0; i < component.SurfacedEditors.Count; i++) - { - var (cellId, cellData) = component.DecodeContent(i); - if (cellId != null) - { - if (startsWith) - { - if (cellData.StartsWith(checkForString)) - { - // WIP: check if the value has actually changed, this will update every object on any cell change - yield return i; - } - } - else - { - if (cellData.Contains(checkForString)) - { - yield return i; - } - } - } - } - } + private static object loadLock = new object(); + private static IObject3D sheetObject; - public static bool HasExpressionWithString(IObject3D itemToCheck, string checkForString, bool startsWith) - { - foreach (var item in itemToCheck.DescendantsAndSelf()) - { - if (GetActiveExpressions(item, checkForString, startsWith).Any() - || (itemToCheck is ComponentObject3D component - && GetComponentExpressions(component, checkForString, startsWith).Any())) + public override Mesh Mesh + { + get + { + if (this.Children.Count == 0) { - // three is one so return true - return true; + lock (loadLock) + { + if (sheetObject == null) + { + sheetObject = MeshContentProvider.LoadMCX(StaticData.Instance.OpenStream(Path.Combine("Stls", "sheet_icon.mcx"))); + } + + this.Children.Modify((list) => + { + list.Clear(); + + list.Add(sheetObject.Clone()); + }); + } } - } - return false; - } + return null; + } - public static T CastResult(string value, string inputExpression) - { - if (typeof(T) == typeof(string)) - { - // if parsing the equation resulted in NaN as the output - if (value == "NaN") - { - // return the actual expression - return (T)(object)inputExpression; - } + set => base.Mesh = value; + } - // get the value of the cell - return (T)(object)value; - } + public SheetObject3D() + { + } - if (typeof(T) == typeof(double)) - { - if (double.TryParse(value, out double doubleValue) - && !double.IsNaN(doubleValue) - && !double.IsInfinity(doubleValue)) - { - return (T)(object)doubleValue; - } - // else return an error - return (T)(object).1; - } + public override bool Printable => false; - if (typeof(T) == typeof(int)) - { - if (double.TryParse(value, out double doubleValue) - && !double.IsNaN(doubleValue) - && !double.IsInfinity(doubleValue)) - { - return (T)(object)(int)Math.Round(doubleValue); - } - // else return an error - return (T)(object)1; - } - - return (T)(object)default(T); - } - } + public class UpdateItem + { + internal int depth; + internal IObject3D item; + internal RebuildLock rebuildLock; + + public override string ToString() + { + var state = rebuildLock == null ? "unlocked" : "locked"; + return $"{depth} {state} - {item}"; + } + } + + private void SendInvalidateToAll(object s, EventArgs e) + { + var updateItems = Expressions.SortAndLockUpdateItems(this.Parent, (item) => + { + if (item == this || item.Parent == this) + { + // don't process this + return false; + } + else if (item.Parent is ArrayObject3D arrayObject3D + && arrayObject3D.SourceContainer != item) + { + // don't process the copied children of an array object + return false; + } + + return true; + }, true); + + Expressions.SendInvalidateInRebuildOrder(updateItems, InvalidateType.SheetUpdated, this); + } + + public string EvaluateExpression(string inputExpression) + { + return SheetData.EvaluateExpression(inputExpression); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Sheets/StringOrExpression.cs b/MatterControlLib/DesignTools/Sheets/StringOrExpression.cs index 7bbb7cffb..248c8ce84 100644 --- a/MatterControlLib/DesignTools/Sheets/StringOrExpression.cs +++ b/MatterControlLib/DesignTools/Sheets/StringOrExpression.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2022, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,29 +32,29 @@ using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.DesignTools { - [TypeConverter(typeof(StringOrExpression))] - public class StringOrExpression : DirectOrExpression - { - public string Value(IObject3D owner) - { - var rebuilding = owner.RebuildLocked; - var value = SheetObject3D.EvaluateExpression(owner, Expression); - if (rebuilding) - { - ExpressionValueAtLastRebuild = value.ToString(); - } + [TypeConverter(typeof(StringOrExpression))] + public class StringOrExpression : DirectOrExpression + { + public string Value(IObject3D owner) + { + var rebuilding = owner.RebuildLocked; + var value = Expressions.EvaluateExpression(owner, Expression); + if (rebuilding) + { + ExpressionValueAtLastRebuild = value.ToString(); + } - return value; - } + return value; + } - public StringOrExpression(string expression) - { - Expression = expression; - } + public StringOrExpression(string expression) + { + Expression = expression; + } - public static implicit operator StringOrExpression(string expression) - { - return new StringOrExpression(expression); - } - } + public static implicit operator StringOrExpression(string expression) + { + return new StringOrExpression(expression); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/SupportGenerator.cs b/MatterControlLib/DesignTools/SupportGenerator.cs index df025d9c5..4fd9a16c0 100644 --- a/MatterControlLib/DesignTools/SupportGenerator.cs +++ b/MatterControlLib/DesignTools/SupportGenerator.cs @@ -34,6 +34,7 @@ using System.Threading; using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.PolygonMesh; using MatterHackers.RayTracer; @@ -143,22 +144,19 @@ namespace MatterHackers.MatterControl.DesignTools return BoundingVolumeHierarchy.CreateNewHierachy(allPolys, bvhCreationOptions); } - public Task Create(IProgress progress, CancellationToken cancellationToken) + public Task Create(Action progress, CancellationToken cancellationToken) { return Create(progress, null); } - public Task Create(IProgress progress, CancellationTokenSource cancellationTokenSource) + public Task Create(Action progress, CancellationTokenSource cancellationTokenSource) { var selectedItem = scene.SelectedItem; using (new DataConverters3D.SelectionMaintainer(scene)) { - var status = new ProgressStatus - { - Status = "Enter" - }; - progress?.Report(status); + var status = "Enter".Localize(); + progress?.Invoke(0, status); // Get visible meshes for each of them var allBedItems = scene.Children.SelectMany(i => i.VisibleMeshes()); @@ -204,12 +202,12 @@ namespace MatterHackers.MatterControl.DesignTools } // get all the support plane intersections - status.Status = "Trace"; - progress?.Report(status); + status = "Trace".Localize(); + progress?.Invoke(0, status); var detectedPlanes = DetectRequiredSupportByTracing(gridBounds, allBedItems); - status.Status = "Columns"; - progress?.Report(status); + status = "Columns".Localize(); + progress?.Invoke(0, status); // minimum height requiring support is 1/2 the layer height AddSupportColumns(gridBounds, detectedPlanes); diff --git a/MatterControlLib/HelpIndex.cs b/MatterControlLib/HelpIndex.cs index 76a30e93d..61ddc31d1 100644 --- a/MatterControlLib/HelpIndex.cs +++ b/MatterControlLib/HelpIndex.cs @@ -100,7 +100,7 @@ namespace MatterControlLib } } - private static void IndexZipFile(string filePath, IProgress progress, CancellationToken cancellationToken) + private static void IndexZipFile(string filePath, Action progress, CancellationToken cancellationToken) { Dictionary helpArticles; @@ -126,19 +126,13 @@ namespace MatterControlLib ProcessHelpTree(rootHelpArticle, helpArticles); } - var progressStatus = new ProgressStatus() - { - //Status = "", - Progress0To1 = 0 - }; - var count = zip.Entries.Count; double i = 0; foreach (var entry in zip.Entries) { - progressStatus.Progress0To1 = i++ / count; - progress.Report(progressStatus); + var progress0To1 = i++ / (double)count; + progress?.Invoke(progress0To1, null); // Observe and abort on cancellationToken signal if (cancellationToken.IsCancellationRequested) diff --git a/MatterControlLib/Library/Export/AmfExport.cs b/MatterControlLib/Library/Export/AmfExport.cs index 89d5dc7a3..c95e8c267 100644 --- a/MatterControlLib/Library/Export/AmfExport.cs +++ b/MatterControlLib/Library/Export/AmfExport.cs @@ -52,7 +52,7 @@ namespace MatterHackers.MatterControl.Library.Export public bool ExportPossible(ILibraryAsset libraryItem) => true; - public async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { var firstItem = libraryItems.OfType().FirstOrDefault(); if (firstItem is ILibraryAsset libraryItem) diff --git a/MatterControlLib/Library/Export/FolderExport.cs b/MatterControlLib/Library/Export/FolderExport.cs index 3ac06fa58..eb795d4f7 100644 --- a/MatterControlLib/Library/Export/FolderExport.cs +++ b/MatterControlLib/Library/Export/FolderExport.cs @@ -63,7 +63,7 @@ namespace MatterHackers.MatterControl.Library.Export public bool ExportPossible(ILibraryAsset libraryItem) => true; - public async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { var streamItems = libraryItems.OfType(); if (streamItems.Any()) diff --git a/MatterControlLib/Library/Export/GCodeExport.cs b/MatterControlLib/Library/Export/GCodeExport.cs index 0ada085d8..ca6fad08c 100644 --- a/MatterControlLib/Library/Export/GCodeExport.cs +++ b/MatterControlLib/Library/Export/GCodeExport.cs @@ -156,7 +156,7 @@ namespace MatterHackers.MatterControl.Library.Export return container; } - public virtual async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public virtual async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { var firstItem = libraryItems.OfType().FirstOrDefault(); if (firstItem != null) @@ -185,16 +185,12 @@ namespace MatterHackers.MatterControl.Library.Export } else if (firstItem is ILibraryObject3D object3DItem) { - var status = new ProgressStatus() - { - Status = "Saving Asset".Localize() - }; + var status = "Saving Asset".Localize(); loadedItem = await object3DItem.CreateContent(null); - await loadedItem.PersistAssets((percentComplete, text) => + await loadedItem.PersistAssets((progress2, text) => { - status.Progress0To1 = percentComplete; - progress.Report(status); + progress?.Invoke(progress2, status); }); } else if (assetStream != null) @@ -340,7 +336,7 @@ namespace MatterHackers.MatterControl.Library.Export return accumulatedStream; } - private void ApplyStreamPipelineAndExport(GCodeFileStream gCodeFileStream, string outputPath, IProgress progress) + private void ApplyStreamPipelineAndExport(GCodeFileStream gCodeFileStream, string outputPath, Action progress) { try { @@ -349,10 +345,7 @@ namespace MatterHackers.MatterControl.Library.Export var totalLines = gCodeFileStream.GCodeFile.LineCount; var currentLine = 0; - var status = new ProgressStatus() - { - Status = "Writing G-Code".Localize() - }; + var status = "Writing G-Code".Localize(); // Run each line from the source gcode through the loaded pipeline and dump to the output location using (var file = new StreamWriter(outputPath)) @@ -387,8 +380,7 @@ namespace MatterHackers.MatterControl.Library.Export if (currentLine % 1024 == 0) { - status.Progress0To1 = currentLine / (double)totalLines; - progress.Report(status); + progress?.Invoke(currentLine / (double)totalLines, status); } currentLine++; } @@ -403,7 +395,7 @@ namespace MatterHackers.MatterControl.Library.Export } } - private void ApplyStreamPipelineAndExport(string gcodeFilename, string outputPath, IProgress progress) + private void ApplyStreamPipelineAndExport(string gcodeFilename, string outputPath, Action progress) { try { diff --git a/MatterControlLib/Library/Export/IExportPlugin.cs b/MatterControlLib/Library/Export/IExportPlugin.cs index 2410b66c8..01511de63 100644 --- a/MatterControlLib/Library/Export/IExportPlugin.cs +++ b/MatterControlLib/Library/Export/IExportPlugin.cs @@ -51,7 +51,7 @@ namespace MatterHackers.MatterControl void Initialize(PrinterConfig printer); - Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken); + Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken); bool Enabled { get; } diff --git a/MatterControlLib/Library/Export/McxExport.cs b/MatterControlLib/Library/Export/McxExport.cs index 34026a640..d3482d5c8 100644 --- a/MatterControlLib/Library/Export/McxExport.cs +++ b/MatterControlLib/Library/Export/McxExport.cs @@ -53,7 +53,7 @@ namespace MatterHackers.MatterControl.Library.Export public bool ExportPossible(ILibraryAsset libraryItem) => true; - public async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { try { diff --git a/MatterControlLib/Library/Export/MeshExport.cs b/MatterControlLib/Library/Export/MeshExport.cs index 0a0d2ca31..b273ed0cf 100644 --- a/MatterControlLib/Library/Export/MeshExport.cs +++ b/MatterControlLib/Library/Export/MeshExport.cs @@ -38,67 +38,69 @@ using MatterHackers.Localizations; namespace MatterHackers.MatterControl.Library.Export { - public static class MeshExport - { - public static async Task ExportMesh(ILibraryItem source, string filePathToSave, bool mergeMeshes, IProgress progress) - { - try - { - var status = new ProgressStatus() - { - Status = "Exporting".Localize() - }; - - if (source is ILibraryObject3D contentItem) - { - // If the content is an IObject3D, then we need to load it and MeshFileIO save to the target path - var content = await contentItem.GetObject3D(null); - return Object3D.Save(content, filePathToSave, mergeMeshes, CancellationToken.None, reportProgress: (ratio, name) => + public static class MeshExport + { + /// + /// Export the source item(s) to a single or multiple STL files + /// + /// The source item(s) + /// The destination to save to (only the folder will be used in saveMultipleStls + /// Do a Combine on the individual meshes to ensure they are a single item + /// Save multiple stls using the name of the objects in the scene + /// Update the save progress + /// If the function succeded + public static async Task ExportMesh(ILibraryItem source, string filePathToSave, bool mergeMeshes, Action progress, bool saveMultipleStls = false) + { + try + { + if (source is ILibraryObject3D contentItem) + { + // If the content is an IObject3D, then we need to load it and MeshFileIO save to the target path + var content = await contentItem.GetObject3D(null); + return Object3D.Save(content, filePathToSave, mergeMeshes, CancellationToken.None, reportProgress: (ratio, name) => { - status.Progress0To1 = ratio; - progress.Report(status); - }); - } - else if (source is ILibraryAssetStream streamContent) - { - if (!string.IsNullOrEmpty(filePathToSave)) - { - // If the file is already the target type, it just needs copied to the target path - if (Path.GetExtension(streamContent.FileName).ToUpper() == Path.GetExtension(filePathToSave).ToUpper()) - { - using (var result = await streamContent.GetStream(null)) - using (var fileStream = File.Create(filePathToSave)) - { - result.Stream.CopyTo(fileStream); - } + progress?.Invoke(ratio, "Exporting".Localize()); + }, saveMultipleStls: saveMultipleStls); + } + else if (source is ILibraryAssetStream streamContent) + { + if (!string.IsNullOrEmpty(filePathToSave)) + { + // If the file is already the target type, it just needs copied to the target path + if (Path.GetExtension(streamContent.FileName).ToUpper() == Path.GetExtension(filePathToSave).ToUpper()) + { + using (var result = await streamContent.GetStream(null)) + using (var fileStream = File.Create(filePathToSave)) + { + result.Stream.CopyTo(fileStream); + } - return true; - } - else - { - // Otherwise we need to load the content and MeshFileIO save to the target path - using (var result = await streamContent.GetStream(null)) - { - IObject3D item = Object3D.Load(result.Stream, Path.GetExtension(streamContent.FileName), CancellationToken.None); - if (item != null) - { - return Object3D.Save(item, filePathToSave, mergeMeshes, CancellationToken.None, reportProgress: (ratio, name) => + return true; + } + else + { + // Otherwise we need to load the content and MeshFileIO save to the target path + using (var result = await streamContent.GetStream(null)) + { + IObject3D item = Object3D.Load(result.Stream, Path.GetExtension(streamContent.FileName), CancellationToken.None); + if (item != null) + { + return Object3D.Save(item, filePathToSave, mergeMeshes, CancellationToken.None, reportProgress: (ratio, name) => { - status.Progress0To1 = ratio; - progress.Report(status); - }); - } - } - } - } - } - } - catch (Exception ex) - { - Trace.WriteLine("Error exporting file: " + ex.Message); - } + progress?.Invoke(ratio, null); + }, saveMultipleStls: saveMultipleStls); + } + } + } + } + } + } + catch (Exception ex) + { + Trace.WriteLine("Error exporting file: " + ex.Message); + } - return false; - } - } + return false; + } + } } diff --git a/MatterControlLib/Library/Export/StlExport.cs b/MatterControlLib/Library/Export/StlExport.cs index af841920d..fafa66d0d 100644 --- a/MatterControlLib/Library/Export/StlExport.cs +++ b/MatterControlLib/Library/Export/StlExport.cs @@ -68,7 +68,7 @@ namespace MatterHackers.MatterControl.Library.Export public async Task> Generate(IEnumerable libraryItems, string outputPath, - IProgress progress, + Action progress, CancellationToken cancellationToken) { var first = true; diff --git a/MatterControlLib/Library/Export/ZipExport.cs b/MatterControlLib/Library/Export/ZipExport.cs index 2d7c20b15..c2a68081c 100644 --- a/MatterControlLib/Library/Export/ZipExport.cs +++ b/MatterControlLib/Library/Export/ZipExport.cs @@ -64,7 +64,7 @@ namespace MatterHackers.MatterControl.Library.Export public bool ExportPossible(ILibraryAsset libraryItem) => true; - public async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { var streamItems = libraryItems.OfType(); if (streamItems.Any()) diff --git a/MatterControlLib/Library/Providers/FileSystem/FileSystemContainer.cs b/MatterControlLib/Library/Providers/FileSystem/FileSystemContainer.cs index c7e1aa499..cfbf8e7d3 100644 --- a/MatterControlLib/Library/Providers/FileSystem/FileSystemContainer.cs +++ b/MatterControlLib/Library/Providers/FileSystem/FileSystemContainer.cs @@ -43,456 +43,449 @@ using MatterHackers.MatterControl.DataStorage; namespace MatterHackers.MatterControl.Library { - public class FileSystemContainer : WritableContainer, ICustomSearch - { - private FileSystemWatcher directoryWatcher; + public class FileSystemContainer : WritableContainer, ICustomSearch + { + private FileSystemWatcher directoryWatcher; - private bool isActiveContainer; - private bool isDirty; - private string keywordFilter; + private bool isActiveContainer; + private bool isDirty; + private string keywordFilter; - private long lastTimeContentsChanged; + private long lastTimeContentsChanged; - private RunningInterval waitingForRefresh; + private RunningInterval waitingForRefresh; - public FileSystemContainer(string fullPath) - { - this.CustomSearch = this; - this.FullPath = fullPath; - this.Name = Path.GetFileName(fullPath); + public FileSystemContainer(string fullPath) + { + this.CustomSearch = this; + this.FullPath = fullPath; + this.Name = Path.GetFileName(fullPath); - this.IsProtected = false; + this.IsProtected = false; - this.ChildContainers = new SafeList(); - this.Items = new SafeList(); + this.ChildContainers = new SafeList(); + this.Items = new SafeList(); - if (AggContext.OperatingSystem == OSType.Windows - && Directory.Exists(fullPath)) - { - directoryWatcher = new FileSystemWatcher(fullPath); - directoryWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; - directoryWatcher.Changed += DirectoryContentsChanged; - directoryWatcher.Created += DirectoryContentsChanged; - directoryWatcher.Deleted += DirectoryContentsChanged; - directoryWatcher.Renamed += DirectoryContentsChanged; - directoryWatcher.IncludeSubdirectories = false; + if (AggContext.OperatingSystem == OSType.Windows + && Directory.Exists(fullPath)) + { + directoryWatcher = new FileSystemWatcher(fullPath); + directoryWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; + directoryWatcher.Changed += DirectoryContentsChanged; + directoryWatcher.Created += DirectoryContentsChanged; + directoryWatcher.Deleted += DirectoryContentsChanged; + directoryWatcher.Renamed += DirectoryContentsChanged; + directoryWatcher.IncludeSubdirectories = false; - // Begin watching. - directoryWatcher.EnableRaisingEvents = true; - } - } + // Begin watching. + directoryWatcher.EnableRaisingEvents = true; + } + } - public override async Task Save(ILibraryItem item, IObject3D content) - { - if (item is FileSystemFileItem fileItem) - { - if (fileItem.FilePath.Contains(ApplicationDataStorage.Instance.ApplicationLibraryDataPath)) - { - // save using the normal uncompressed mcx file - // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters - File.WriteAllText(fileItem.FilePath, content.ToJson().Result); - - this.OnItemContentChanged(new LibraryItemChangedEventArgs(fileItem)); - } - else - { - await ApplicationController.Instance.Tasks.Execute( - "Saving Changes".Localize(), - null, - async (reporter, cancellationTokenSource) => - { - var status = new ProgressStatus() - { - Status = "Saving Asset".Localize() - }; - - var directory = Path.GetDirectoryName(fileItem.FilePath); - var filename = Path.GetFileNameWithoutExtension(fileItem.FilePath); - var backupName = Path.Combine(directory, Path.ChangeExtension(filename + "_bak", ".mcx")); - - try - { - if (File.Exists(backupName)) - { - File.Delete(backupName); - } - - // rename any existing file - if (File.Exists(fileItem.FilePath)) - { - File.Move(fileItem.FilePath, backupName); - } - } - catch - { - } - - // make sure we have all the mesh items in the cache for saving to the archive - await content.PersistAssets((percentComplete, text) => - { - status.Progress0To1 = percentComplete * .9; - reporter.Report(status); - }, true); - - var persistableItems = content.GetPersistable(true); - var persistCount = persistableItems.Count(); - var savedCount = 0; - - // save to a binary mcx file (a zip with a scene.mcx and an assets folder) - using (var file = File.OpenWrite(fileItem.FilePath)) - { - using (var zip = new ZipArchive(file, ZipArchiveMode.Create)) - { - var savedItems = new HashSet(); - foreach (var persistableItem in persistableItems) - { - string sourcePath = null; - if (persistableItem.MeshPath != null) - { - sourcePath = Path.Combine(ApplicationDataStorage.Instance.LibraryAssetsPath, Path.GetFileName(persistableItem.MeshPath)); - } - - if (persistableItem is AssetObject3D assetObject3D) - { - sourcePath = Path.Combine(ApplicationDataStorage.Instance.LibraryAssetsPath, Path.GetFileName(assetObject3D.AssetPath)); - } - - var destFilename = Path.GetFileName(sourcePath); - if (File.Exists(sourcePath)) - { - if (!savedItems.Contains(destFilename)) - { - savedItems.Add(destFilename); - var assetName = Path.Combine("Assets", destFilename); - zip.CreateEntryFromFile(sourcePath, assetName); - } - } - else - { - int a = 0; - } - - savedCount++; - status.Progress0To1 = .9 + .1 * (savedCount / persistCount); - reporter.Report(status); - } - - var sceneEntry = zip.CreateEntry("scene.mcx"); - using (var sceneStream = sceneEntry.Open()) - { - using (var writer = new StreamWriter(sceneStream)) - { - writer.Write(await content.ToJson()); - } - } - } - } - - // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters - this.OnItemContentChanged(new LibraryItemChangedEventArgs(fileItem)); - - // remove the existing file after a successfull save - try - { - if (File.Exists(backupName)) - { - File.Delete(backupName); - } - } - catch { } - }); - } - } - } - - public override void SetThumbnail(ILibraryItem item, int width, int height, ImageBuffer imageBuffer) - { -#if DEBUG - // throw new NotImplementedException(); -#endif - } - - public override ICustomSearch CustomSearch { get; } - - public string FullPath { get; protected set; } - - // Indicates if the new AMF file should use the original file name incremented until no name collision occurs - public bool UseIncrementedNameDuringTypeChange { get; internal set; } - - public override void Activate() - { - this.isActiveContainer = true; - base.Activate(); - } - - public async override void Add(IEnumerable items) - { - if (!items.Any()) - { - return; - } - - if (directoryWatcher != null) - { - // turn them off whil ewe add the content - directoryWatcher.EnableRaisingEvents = false; - } - - Directory.CreateDirectory(this.FullPath); - - await Task.Run(async () => - { - foreach (var item in items) - { - switch (item) - { - case CreateFolderItem newFolder: - string targetFolderPath = Path.Combine(this.FullPath, newFolder.Name); - - // TODO: write adaption of GetNonCollidingName for directories - Directory.CreateDirectory(targetFolderPath); - this.isDirty = true; - - break; - - case ILibraryAssetStream streamItem: - string targetPath = Path.Combine(this.FullPath, streamItem.FileName); - - try - { - if (File.Exists(targetPath)) - { - targetPath = GetNonCollidingName(Path.GetFileName(targetPath)); - } - - using (var outputStream = File.OpenWrite(targetPath)) - using (var contentStream = await streamItem.GetStream(null)) - { - contentStream.Stream.CopyTo(outputStream); - } - - this.Items.Add(new FileSystemFileItem(targetPath)); - this.isDirty = true; - } - catch (Exception ex) - { - UiThread.RunOnIdle(() => - { - ApplicationController.Instance.LogError($"Error adding file: {targetPath}\r\n{ex.Message}"); - }); - } - - break; - } - } - }); - - if (directoryWatcher != null) - { - // turn them back on - directoryWatcher.EnableRaisingEvents = true; - } - - if (this.isDirty) - { - this.ReloadContent(); - } - } - - public void ApplyFilter(string filter, ILibraryContext libraryContext) - { - keywordFilter = filter; - this.Load(); - this.OnContentChanged(); - } - - public void ClearFilter() - { - keywordFilter = null; - this.Load(); - this.OnContentChanged(); - } - - public override void Deactivate() - { - this.isActiveContainer = false; - base.Deactivate(); - } - - public override void Dispose() - { - if (directoryWatcher != null) - { - directoryWatcher.EnableRaisingEvents = false; - - directoryWatcher.Changed -= DirectoryContentsChanged; - directoryWatcher.Created -= DirectoryContentsChanged; - directoryWatcher.Deleted -= DirectoryContentsChanged; - directoryWatcher.Renamed -= DirectoryContentsChanged; - } - } - - public override void Load() - { - this.Load(false); - } - - public void Load(bool recursive) - { - SearchOption searchDepth = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - - try - { - string filter = keywordFilter?.Trim() ?? ""; - - var allFiles = Directory.GetFiles(FullPath, "*.*", searchDepth); - - var zipFiles = allFiles.Where(f => Path.GetExtension(f).IndexOf(".zip", StringComparison.OrdinalIgnoreCase) != -1); - - var nonZipFiles = allFiles.Except(zipFiles); - - if (filter == "") - { - var directories = Directory.GetDirectories(FullPath, "*.*", searchDepth).Select(p => new DirectoryContainerLink(p)).ToList(); - this.ChildContainers = new SafeList(directories.Concat(zipFiles.Select(f => new LocalZipContainerLink(f)))); - var libraryFiles = allFiles.Where(f => Path.GetExtension(f).IndexOf(".library", StringComparison.OrdinalIgnoreCase) != -1); - this.ChildContainers.AddRange(libraryFiles.Select(f => LibraryJsonFile.ContainerFromLocalFile(f))); - } - else - { - this.ChildContainers = new SafeList(); - } - - var matchedFiles = nonZipFiles.Where(filePath => - { - string fileName = Path.GetFileName(filePath); - - return (filter == "" || FileNameContainsFilter(filePath, filter)) - && ApplicationController.Instance.Library.IsContentFileType(fileName); - }); - - this.ChildContainers.Modify((list) => - { - list.Sort((a, b) => a.Name.CompareTo(b.Name)); - }); - - // Matched files projected onto FileSystemFileItem - this.Items = new SafeList(matchedFiles.OrderBy(f => f).Select(f => new FileSystemFileItem(f))); - - var indexMd = nonZipFiles.Where(f => f.EndsWith("index.md")).FirstOrDefault(); - if (indexMd != null) + public override async Task Save(ILibraryItem item, IObject3D content) + { + if (item is FileSystemFileItem fileItem) + { + if (fileItem.FilePath.Contains(ApplicationDataStorage.Instance.ApplicationLibraryDataPath)) { - try - { - HeaderMarkdown = File.ReadAllText(indexMd); - } - catch { } - } + // save using the normal uncompressed mcx file + // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters + File.WriteAllText(fileItem.FilePath, content.ToJson().Result); - this.isDirty = false; - } - catch (Exception ex) - { - this.ChildContainers = new SafeList(); - this.Items = new SafeList() - { - new MessageItem("Error loading container - " + ex.Message) - }; - } - } + this.OnItemContentChanged(new LibraryItemChangedEventArgs(fileItem)); + } + else + { + await ApplicationController.Instance.Tasks.Execute( + "Saving Changes".Localize(), + null, + async (reporter, cancellationTokenSource) => + { + var directory = Path.GetDirectoryName(fileItem.FilePath); + var filename = Path.GetFileNameWithoutExtension(fileItem.FilePath); + var backupName = Path.Combine(directory, Path.ChangeExtension(filename + "_bak", ".mcx")); - public override void Remove(IEnumerable items) - { - if (AggContext.OperatingSystem == OSType.Windows) - { - foreach (var item in items) - { - if (item is FileSystemItem fileItem - && File.Exists(fileItem.FilePath)) - { - File.Delete(fileItem.FilePath); - } - } + try + { + if (File.Exists(backupName)) + { + File.Delete(backupName); + } - this.ReloadContent(); - } - } + // rename any existing file + if (File.Exists(fileItem.FilePath)) + { + File.Move(fileItem.FilePath, backupName); + } + } + catch + { + } - private void DirectoryContentsChanged(object sender, EventArgs e) - { - // Flag for reload - isDirty = true; + // make sure we have all the mesh items in the cache for saving to the archive + await content.PersistAssets((percentComplete, text) => + { + reporter?.Invoke(percentComplete * .9, null); + }, true); - lastTimeContentsChanged = UiThread.CurrentTimerMs; + var persistableItems = content.GetPersistable(true); + var persistCount = persistableItems.Count(); + var savedCount = 0; - // Only refresh content if we're the active container - if (isActiveContainer - && waitingForRefresh == null) - { - waitingForRefresh = UiThread.SetInterval(WaitToRefresh, .5); - } - } + // save to a binary mcx file (a zip with a scene.mcx and an assets folder) + using (var file = File.OpenWrite(fileItem.FilePath)) + { + using (var zip = new ZipArchive(file, ZipArchiveMode.Create)) + { + var savedItems = new HashSet(); + foreach (var persistableItem in persistableItems) + { + string sourcePath = null; + if (persistableItem.MeshPath != null) + { + sourcePath = Path.Combine(ApplicationDataStorage.Instance.LibraryAssetsPath, Path.GetFileName(persistableItem.MeshPath)); + } - private bool FileNameContainsFilter(string filename, string filter) - { - string nameWithSpaces = Path.GetFileNameWithoutExtension(filename.Replace('_', ' ')); + if (persistableItem is AssetObject3D assetObject3D) + { + sourcePath = Path.Combine(ApplicationDataStorage.Instance.LibraryAssetsPath, Path.GetFileName(assetObject3D.AssetPath)); + } - // Split the filter on word boundaries and determine if all terms in the given file name - foreach (string word in filter.Split(' ')) - { - if (nameWithSpaces.IndexOf(word, StringComparison.OrdinalIgnoreCase) == -1) - { - return false; - } - } + var destFilename = Path.GetFileName(sourcePath); + if (File.Exists(sourcePath)) + { + if (!savedItems.Contains(destFilename)) + { + savedItems.Add(destFilename); + var assetName = Path.Combine("Assets", destFilename); + zip.CreateEntryFromFile(sourcePath, assetName); + } + } + else + { + int a = 0; + } - return true; - } + savedCount++; + reporter?.Invoke(.9 + .1 * (savedCount / persistCount), null); + } - private string GetNonCollidingName(string fileName) - { - // Switching from .stl, .obj or similar to AMF. Save the file and update the - // the filename with an incremented (n) value to reflect the extension change in the UI - var similarFileNames = Directory.GetFiles(this.FullPath, $"{Path.GetFileNameWithoutExtension(fileName)}.*"); + var sceneEntry = zip.CreateEntry("scene.mcx"); + using (var sceneStream = sceneEntry.Open()) + { + using (var writer = new StreamWriter(sceneStream)) + { + writer.Write(await content.ToJson()); + } + } + } + } - // ; - var validName = agg_basics.GetNonCollidingName(fileName, (testName) => !File.Exists(testName)); + // Serialize the scene to disk using a modified Json.net pipeline with custom ContractResolvers and JsonConverters + this.OnItemContentChanged(new LibraryItemChangedEventArgs(fileItem)); - return validName; - } + // remove the existing file after a successfull save + try + { + if (File.Exists(backupName)) + { + File.Delete(backupName); + } + } + catch { } + }); + } + } + } - private void WaitToRefresh() - { - if (UiThread.CurrentTimerMs > lastTimeContentsChanged + 500 - && waitingForRefresh != null) - { - UiThread.ClearInterval(waitingForRefresh); + public override void SetThumbnail(ILibraryItem item, int width, int height, ImageBuffer imageBuffer) + { +#if DEBUG + // throw new NotImplementedException(); +#endif + } - waitingForRefresh = null; - this.ReloadContent(); - } - } + public override ICustomSearch CustomSearch { get; } - public class DirectoryContainerLink : FileSystemItem, ILibraryContainerLink - { - public DirectoryContainerLink(string path) - : base(path) - { - } + public string FullPath { get; protected set; } - public bool IsReadOnly { get; set; } = false; + // Indicates if the new AMF file should use the original file name incremented until no name collision occurs + public bool UseIncrementedNameDuringTypeChange { get; internal set; } - public bool UseIncrementedNameDuringTypeChange { get; set; } + public override void Activate() + { + this.isActiveContainer = true; + base.Activate(); + } - public Task GetContainer(Action reportProgress) - { - return Task.FromResult( - new FileSystemContainer(this.FilePath) - { - UseIncrementedNameDuringTypeChange = this.UseIncrementedNameDuringTypeChange, - Name = this.Name - }); - } - } - } + public async override void Add(IEnumerable items) + { + if (!items.Any()) + { + return; + } + + if (directoryWatcher != null) + { + // turn them off whil ewe add the content + directoryWatcher.EnableRaisingEvents = false; + } + + Directory.CreateDirectory(this.FullPath); + + await Task.Run(async () => + { + foreach (var item in items) + { + switch (item) + { + case CreateFolderItem newFolder: + string targetFolderPath = Path.Combine(this.FullPath, newFolder.Name); + + // TODO: write adaption of GetNonCollidingName for directories + Directory.CreateDirectory(targetFolderPath); + this.isDirty = true; + + break; + + case ILibraryAssetStream streamItem: + string targetPath = Path.Combine(this.FullPath, streamItem.FileName); + + try + { + if (File.Exists(targetPath)) + { + targetPath = GetNonCollidingName(Path.GetFileName(targetPath)); + } + + using (var outputStream = File.OpenWrite(targetPath)) + using (var contentStream = await streamItem.GetStream(null)) + { + contentStream.Stream.CopyTo(outputStream); + } + + this.Items.Add(new FileSystemFileItem(targetPath)); + this.isDirty = true; + } + catch (Exception ex) + { + UiThread.RunOnIdle(() => + { + ApplicationController.Instance.LogError($"Error adding file: {targetPath}\r\n{ex.Message}"); + }); + } + + break; + } + } + }); + + if (directoryWatcher != null) + { + // turn them back on + directoryWatcher.EnableRaisingEvents = true; + } + + if (this.isDirty) + { + this.ReloadContent(); + } + } + + public void ApplyFilter(string filter, ILibraryContext libraryContext) + { + keywordFilter = filter; + this.Load(); + this.OnContentChanged(); + } + + public void ClearFilter() + { + keywordFilter = null; + this.Load(); + this.OnContentChanged(); + } + + public override void Deactivate() + { + this.isActiveContainer = false; + base.Deactivate(); + } + + public override void Dispose() + { + if (directoryWatcher != null) + { + directoryWatcher.EnableRaisingEvents = false; + + directoryWatcher.Changed -= DirectoryContentsChanged; + directoryWatcher.Created -= DirectoryContentsChanged; + directoryWatcher.Deleted -= DirectoryContentsChanged; + directoryWatcher.Renamed -= DirectoryContentsChanged; + } + } + + public override void Load() + { + this.Load(false); + } + + public void Load(bool recursive) + { + SearchOption searchDepth = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + + try + { + string filter = keywordFilter?.Trim() ?? ""; + + var allFiles = Directory.GetFiles(FullPath, "*.*", searchDepth); + + var zipFiles = allFiles.Where(f => Path.GetExtension(f).IndexOf(".zip", StringComparison.OrdinalIgnoreCase) != -1); + + var nonZipFiles = allFiles.Except(zipFiles); + + if (filter == "") + { + var directories = Directory.GetDirectories(FullPath, "*.*", searchDepth).Select(p => new DirectoryContainerLink(p)).ToList(); + this.ChildContainers = new SafeList(directories.Concat(zipFiles.Select(f => new LocalZipContainerLink(f)))); + var libraryFiles = allFiles.Where(f => Path.GetExtension(f).IndexOf(".library", StringComparison.OrdinalIgnoreCase) != -1); + this.ChildContainers.AddRange(libraryFiles.Select(f => LibraryJsonFile.ContainerFromLocalFile(f))); + } + else + { + this.ChildContainers = new SafeList(); + } + + var matchedFiles = nonZipFiles.Where(filePath => + { + string fileName = Path.GetFileName(filePath); + + return (filter == "" || FileNameContainsFilter(filePath, filter)) + && ApplicationController.Instance.Library.IsContentFileType(fileName); + }); + + this.ChildContainers.Modify((list) => + { + list.Sort((a, b) => a.Name.CompareTo(b.Name)); + }); + + // Matched files projected onto FileSystemFileItem + this.Items = new SafeList(matchedFiles.OrderBy(f => f).Select(f => new FileSystemFileItem(f))); + + var indexMd = nonZipFiles.Where(f => f.EndsWith("index.md")).FirstOrDefault(); + if (indexMd != null) + { + try + { + HeaderMarkdown = File.ReadAllText(indexMd); + } + catch { } + } + + this.isDirty = false; + } + catch (Exception ex) + { + this.ChildContainers = new SafeList(); + this.Items = new SafeList() + { + new MessageItem("Error loading container - " + ex.Message) + }; + } + } + + public override void Remove(IEnumerable items) + { + if (AggContext.OperatingSystem == OSType.Windows) + { + foreach (var item in items) + { + if (item is FileSystemItem fileItem + && File.Exists(fileItem.FilePath)) + { + File.Delete(fileItem.FilePath); + } + } + + this.ReloadContent(); + } + } + + private void DirectoryContentsChanged(object sender, EventArgs e) + { + // Flag for reload + isDirty = true; + + lastTimeContentsChanged = UiThread.CurrentTimerMs; + + // Only refresh content if we're the active container + if (isActiveContainer + && waitingForRefresh == null) + { + waitingForRefresh = UiThread.SetInterval(WaitToRefresh, .5); + } + } + + private bool FileNameContainsFilter(string filename, string filter) + { + string nameWithSpaces = Path.GetFileNameWithoutExtension(filename.Replace('_', ' ')); + + // Split the filter on word boundaries and determine if all terms in the given file name + foreach (string word in filter.Split(' ')) + { + if (nameWithSpaces.IndexOf(word, StringComparison.OrdinalIgnoreCase) == -1) + { + return false; + } + } + + return true; + } + + private string GetNonCollidingName(string fileName) + { + // Switching from .stl, .obj or similar to AMF. Save the file and update the + // the filename with an incremented (n) value to reflect the extension change in the UI + var similarFileNames = Directory.GetFiles(this.FullPath, $"{Path.GetFileNameWithoutExtension(fileName)}.*"); + + // ; + var validName = Util.GetNonCollidingName(fileName, (testName) => !File.Exists(testName)); + + return validName; + } + + private void WaitToRefresh() + { + if (UiThread.CurrentTimerMs > lastTimeContentsChanged + 500 + && waitingForRefresh != null) + { + UiThread.ClearInterval(waitingForRefresh); + + waitingForRefresh = null; + this.ReloadContent(); + } + } + + public class DirectoryContainerLink : FileSystemItem, ILibraryContainerLink + { + public DirectoryContainerLink(string path) + : base(path) + { + } + + public bool IsReadOnly { get; set; } = false; + + public bool UseIncrementedNameDuringTypeChange { get; set; } + + public Task GetContainer(Action reportProgress) + { + return Task.FromResult( + new FileSystemContainer(this.FilePath) + { + UseIncrementedNameDuringTypeChange = this.UseIncrementedNameDuringTypeChange, + Name = this.Name + }); + } + } + } } \ No newline at end of file diff --git a/MatterControlLib/Library/Providers/FileSystem/FileSystemItem.cs b/MatterControlLib/Library/Providers/FileSystem/FileSystemItem.cs index eccabcee4..f038ab366 100644 --- a/MatterControlLib/Library/Providers/FileSystem/FileSystemItem.cs +++ b/MatterControlLib/Library/Providers/FileSystem/FileSystemItem.cs @@ -75,7 +75,7 @@ namespace MatterHackers.MatterControl.Library public DateTime DateModified { get; } - public virtual string ID => agg_basics.GetLongHashCode(this.FilePath).ToString(); + public virtual string ID => Util.GetLongHashCode(this.FilePath).ToString(); public virtual bool IsProtected => false; diff --git a/MatterControlLib/Library/Providers/GitHub/GitHubContainer.cs b/MatterControlLib/Library/Providers/GitHub/GitHubContainer.cs index d52ec93c8..a7422ad34 100644 --- a/MatterControlLib/Library/Providers/GitHub/GitHubContainer.cs +++ b/MatterControlLib/Library/Providers/GitHub/GitHubContainer.cs @@ -317,7 +317,7 @@ namespace MatterHackers.MatterControl.Library public long FileSize { get; } = -1; - public string ID => agg_basics.GetLongHashCode(AssetPath).ToString(); + public string ID => Util.GetLongHashCode(AssetPath).ToString(); public bool IsProtected => true; diff --git a/MatterControlLib/Library/Providers/GitHub/GitHubLibraryItem.cs b/MatterControlLib/Library/Providers/GitHub/GitHubLibraryItem.cs index 56a4e63b5..ff3d13257 100644 --- a/MatterControlLib/Library/Providers/GitHub/GitHubLibraryItem.cs +++ b/MatterControlLib/Library/Providers/GitHub/GitHubLibraryItem.cs @@ -58,7 +58,7 @@ namespace MatterHackers.MatterControl.Library public long FileSize { get; private set; } - public string ID => agg_basics.GetLongHashCode(Url).ToString(); + public string ID => Util.GetLongHashCode(Url).ToString(); public bool IsLocked { get; internal set; } diff --git a/MatterControlLib/Library/Providers/MatterControl/CalibrationPartsContainer.cs b/MatterControlLib/Library/Providers/MatterControl/CalibrationPartsContainer.cs index 77e5a3244..53093858f 100644 --- a/MatterControlLib/Library/Providers/MatterControl/CalibrationPartsContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/CalibrationPartsContainer.cs @@ -104,7 +104,7 @@ namespace MatterHackers.MatterControl.Library public string Category { get; } = ""; - public string ID => agg_basics.GetLongHashCode(AssetPath).ToString(); + public string ID => Util.GetLongHashCode(AssetPath).ToString(); public event EventHandler NameChanged; diff --git a/MatterControlLib/Library/Providers/MatterControl/DesignAppsCollectionContainer.cs b/MatterControlLib/Library/Providers/MatterControl/DesignAppsCollectionContainer.cs index f1bfebfdc..076e77e1a 100644 --- a/MatterControlLib/Library/Providers/MatterControl/DesignAppsCollectionContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/DesignAppsCollectionContainer.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2022, John Lewin, Lars Brubaker +Copyright (c) 2023, John Lewin, Lars Brubaker All rights reserved. Redistribution and use in source and binary forms, with or without @@ -48,34 +48,26 @@ namespace MatterHackers.MatterControl.Library this.ChildContainers.Add( new DynamicContainerLink( - "Calibration Parts".Localize(), - StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")), - StaticData.Instance.LoadIcon(Path.Combine("Library", "calibration_library_icon.png")), - () => new CalibrationPartsContainer()) - { - IsReadOnly = true - }); - - this.ChildContainers.Add( - new DynamicContainerLink( - "Scripting".Localize(), - StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")), - StaticData.Instance.LoadIcon(Path.Combine("Library", "scripting_icon.png")), - () => new ScriptingPartsContainer()) - { - IsReadOnly = true - }); - - this.ChildContainers.Add( - new DynamicContainerLink( - "Primitives".Localize(), + "Primitives 3D".Localize(), StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")), StaticData.Instance.LoadIcon(Path.Combine("Library", "primitives_library_icon.png")), - () => new PrimitivesContainer()) + () => new Primitives3DContainer()) { IsReadOnly = true }); +#if DEBUG + this.ChildContainers.Add( + new DynamicContainerLink( + "Primitives 2D".Localize(), + StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")), + StaticData.Instance.LoadIcon(Path.Combine("Library", "primitives_library_icon.png")), + () => new Primitives2DContainer()) + { + IsReadOnly = true + }); +#endif + #if DEBUG int index = 0; @@ -114,10 +106,11 @@ namespace MatterHackers.MatterControl.Library return Task.FromResult(path); }) { DateCreated = new System.DateTime(index++) }, - } + }, + Name = "Experimental".Localize() }) { - IsReadOnly = true + IsReadOnly = true, }); #endif } diff --git a/MatterControlLib/Library/Providers/MatterControl/Primitives2DContainer.cs b/MatterControlLib/Library/Providers/MatterControl/Primitives2DContainer.cs new file mode 100644 index 000000000..aa0917a32 --- /dev/null +++ b/MatterControlLib/Library/Providers/MatterControl/Primitives2DContainer.cs @@ -0,0 +1,100 @@ +/* +Copyright (c) 2019, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using System; +using System.Collections.Generic; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools; +using MatterHackers.MatterControl.DesignTools.Primitives; + +namespace MatterHackers.MatterControl.Library +{ + public class Primitives2DContainer : LibraryContainer + { + public Primitives2DContainer() + { + Name = "Primitives 2D".Localize(); + DefaultSort = new LibrarySortBehavior() + { + SortKey = SortKey.ModifiedDate, + Ascending = true, + }; + } + + public override void Load() + { + var library = ApplicationController.Instance.Library; + + long index = DateTime.Now.Ticks; + var libraryItems = new List() + { + new GeneratorItem( + "Box".Localize(), + async () => await BoxPathObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Triangle".Localize(), + async () => await PyramidObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Trapezoid".Localize(), + async () => await WedgeObject3D_2.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Text".Localize(), + async () => await TextObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Oval".Localize(), + async () => await CylinderObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Star".Localize(), + async () => await ConeObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Ring".Localize(), + async () => await RingObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "Circle".Localize(), + async () => await SphereObject3D.Create()) + { DateCreated = new DateTime(index++) }, + }; + + string title = "2D Shapes".Localize(); + + foreach (var item in libraryItems) + { + item.Category = title; + Items.Add(item); + } + } + } +} diff --git a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs index 63789d6f7..1917a15c5 100644 --- a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs @@ -40,9 +40,9 @@ using MatterHackers.MatterControl.DesignTools.Operations; namespace MatterHackers.MatterControl.Library { - public class PrimitivesContainer : LibraryContainer + public class Primitives3DContainer : LibraryContainer { - public PrimitivesContainer() + public Primitives3DContainer() { Name = "Primitives".Localize(); DefaultSort = new LibrarySortBehavior() @@ -81,7 +81,7 @@ namespace MatterHackers.MatterControl.Library { DateCreated = new DateTime(index++) }, new GeneratorItem( "Text".Localize(), - async () => await TextObject3D.Create(true)) + async () => await TextObject3D.Create()) { DateCreated = new DateTime(index++) }, new GeneratorItem( "Cylinder".Localize(), @@ -188,67 +188,4 @@ namespace MatterHackers.MatterControl.Library } } - - public class Primitives2DContainer : LibraryContainer - { - public Primitives2DContainer() - { - Name = "Primitives 2D".Localize(); - DefaultSort = new LibrarySortBehavior() - { - SortKey = SortKey.ModifiedDate, - Ascending = true, - }; - } - - public override void Load() - { - var library = ApplicationController.Instance.Library; - - long index = DateTime.Now.Ticks; - var libraryItems = new List() - { - new GeneratorItem( - "Box".Localize(), - async () => await BoxPathObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Triangle".Localize(), - async () => await PyramidObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Trapezoid".Localize(), - async () => await WedgeObject3D_2.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Text".Localize(), - async () => await TextObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Oval".Localize(), - async () => await CylinderObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Star".Localize(), - async () => await ConeObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Ring".Localize(), - async () => await RingObject3D.Create()) - { DateCreated = new DateTime(index++) }, - new GeneratorItem( - "Circle".Localize(), - async () => await SphereObject3D.Create()) - { DateCreated = new DateTime(index++) }, - }; - - string title = "2D Shapes".Localize(); - - foreach (var item in libraryItems) - { - item.Category = title; - Items.Add(item); - } - } - } } diff --git a/MatterControlLib/Library/Providers/MatterControl/ScriptingPartsContainer.cs b/MatterControlLib/Library/Providers/MatterControl/ScriptingPartsContainer.cs index 018bf6f8b..ccaadc6cd 100644 --- a/MatterControlLib/Library/Providers/MatterControl/ScriptingPartsContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/ScriptingPartsContainer.cs @@ -102,7 +102,7 @@ namespace MatterHackers.MatterControl.Library public string Category { get; } = ""; - public string ID => agg_basics.GetLongHashCode(AssetPath).ToString(); + public string ID => Util.GetLongHashCode(AssetPath).ToString(); public event EventHandler NameChanged; diff --git a/MatterControlLib/Library/Providers/Zip/ZipMemoryItem.cs b/MatterControlLib/Library/Providers/Zip/ZipMemoryItem.cs index 0989af046..1b6b62b22 100644 --- a/MatterControlLib/Library/Providers/Zip/ZipMemoryItem.cs +++ b/MatterControlLib/Library/Providers/Zip/ZipMemoryItem.cs @@ -60,7 +60,7 @@ namespace MatterHackers.MatterControl.Library /// public long FileSize { get; private set; } - public override string ID => agg_basics.GetLongHashCode($"{this.FilePath}/{this.RelativePath}").ToString(); + public override string ID => Util.GetLongHashCode($"{this.FilePath}/{this.RelativePath}").ToString(); public ZipMemoryContainer ContainingZip { get; } public string RelativePath { get; set; } diff --git a/MatterControlLib/Library/RemoteLibraryItem.cs b/MatterControlLib/Library/RemoteLibraryItem.cs index bb78de21d..4525edea4 100644 --- a/MatterControlLib/Library/RemoteLibraryItem.cs +++ b/MatterControlLib/Library/RemoteLibraryItem.cs @@ -46,7 +46,7 @@ namespace MatterHackers.MatterControl.Library this.url = url; this.Name = name ?? "Unknown".Localize(); - this.ID = agg_basics.GetLongHashCode(url).ToString(); + this.ID = Util.GetLongHashCode(url).ToString(); } public string ID { get; set; } diff --git a/MatterControlLib/Library/Widgets/AddPrinterWidget.cs b/MatterControlLib/Library/Widgets/AddPrinterWidget.cs index d8e6d8294..7970690ef 100644 --- a/MatterControlLib/Library/Widgets/AddPrinterWidget.cs +++ b/MatterControlLib/Library/Widgets/AddPrinterWidget.cs @@ -329,7 +329,7 @@ namespace MatterHackers.MatterControl.Library.Widgets { string printerName = treeView.SelectedNode.Tag.ToString(); - printerNameInput.Text = agg_basics.GetNonCollidingName(printerName, this.ExistingPrinterNames); + printerNameInput.Text = Util.GetNonCollidingName(printerName, this.ExistingPrinterNames); this.SelectedPrinter = treeView.SelectedNode.Tag as MakeModelInfo; diff --git a/MatterControlLib/Library/Widgets/LibraryWidget.cs b/MatterControlLib/Library/Widgets/LibraryWidget.cs index 361a923ae..f41ee30f2 100644 --- a/MatterControlLib/Library/Widgets/LibraryWidget.cs +++ b/MatterControlLib/Library/Widgets/LibraryWidget.cs @@ -786,8 +786,6 @@ namespace MatterHackers.MatterControl.Library.Widgets null, async (reporter, cancellationTokenSource) => { - var progressStatus = new ProgressStatus(); - // Change loaded scene to new context await printer.Bed.LoadContent( new EditContext() @@ -796,12 +794,7 @@ namespace MatterHackers.MatterControl.Library.Widgets // No content store for GCode ContentStore = null }, - (progress, message) => - { - progressStatus.Progress0To1 = progress; - progressStatus.Status = message; - reporter.Report(progressStatus); - }).ConfigureAwait(false); + reporter).ConfigureAwait(false); }); } else diff --git a/MatterControlLib/Library/Widgets/ListView/LibraryListView.cs b/MatterControlLib/Library/Widgets/ListView/LibraryListView.cs index 3f5a793bf..85d065f5e 100644 --- a/MatterControlLib/Library/Widgets/ListView/LibraryListView.cs +++ b/MatterControlLib/Library/Widgets/ListView/LibraryListView.cs @@ -611,7 +611,6 @@ namespace MatterHackers.MatterControl.CustomWidgets null, async (reporter, cancellationTokenSource) => { - var progressStatus = new ProgressStatus(); var editContext = new EditContext() { ContentStore = writableContainer, @@ -619,9 +618,7 @@ namespace MatterHackers.MatterControl.CustomWidgets }; await workspace.SceneContext.LoadContent(editContext, (progress, message) => { - progressStatus.Progress0To1 = progress; - progressStatus.Status = message; - reporter.Report(progressStatus); + reporter?.Invoke(progress, message); }); }); } diff --git a/MatterControlLib/MatterControlLib.csproj b/MatterControlLib/MatterControlLib.csproj index 535a39703..2ad6bc702 100644 --- a/MatterControlLib/MatterControlLib.csproj +++ b/MatterControlLib/MatterControlLib.csproj @@ -93,17 +93,17 @@ - + - - + + - + - + diff --git a/MatterControlLib/PartPreviewWindow/LeftClipFlowLayoutWidget.cs b/MatterControlLib/PartPreviewWindow/LeftClipFlowLayoutWidget.cs index 3b670b6fb..e061a0f35 100644 --- a/MatterControlLib/PartPreviewWindow/LeftClipFlowLayoutWidget.cs +++ b/MatterControlLib/PartPreviewWindow/LeftClipFlowLayoutWidget.cs @@ -68,7 +68,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (this.GradientDistance > 0) { - gradientBackground = agg_basics.TrasparentToColorGradientX( + gradientBackground = Util.TrasparentToColorGradientX( (int)this.LocalBounds.Width + this.GradientDistance, (int)this.LocalBounds.Height, this.BackgroundColor, diff --git a/MatterControlLib/PartPreviewWindow/RunningTaskRow.cs b/MatterControlLib/PartPreviewWindow/RunningTaskRow.cs index 76397be2f..09cc6d238 100644 --- a/MatterControlLib/PartPreviewWindow/RunningTaskRow.cs +++ b/MatterControlLib/PartPreviewWindow/RunningTaskRow.cs @@ -282,9 +282,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow taskDetails.ProgressChanged -= TaskDetails_ProgressChanged; } - private void TaskDetails_ProgressChanged(object sender, ProgressStatus e) - { - if (expandButton.Text != e.Status + private void TaskDetails_ProgressChanged(object sender, (double Progress0To1, string Status) e) + { + if (expandButton.Text != e.Status && !string.IsNullOrEmpty(e.Status) && !expandButton.Text.Contains(e.Status, StringComparison.OrdinalIgnoreCase)) { diff --git a/MatterControlLib/PartPreviewWindow/RunningTaskStatusPanel.cs b/MatterControlLib/PartPreviewWindow/RunningTaskStatusPanel.cs index c58868223..992ad191a 100644 --- a/MatterControlLib/PartPreviewWindow/RunningTaskStatusPanel.cs +++ b/MatterControlLib/PartPreviewWindow/RunningTaskStatusPanel.cs @@ -88,24 +88,29 @@ namespace MatterHackers.MatterControl.PartPreviewWindow taskDetails.ProgressChanged -= TaskDetails_ProgressChanged; } - private void TaskDetails_ProgressChanged(object sender, ProgressStatus e) - { - if (e.Target == "terminal" - && sender is RunningTaskDetails details - && details.Owner is PrinterConfig printer) + private void TaskDetails_ProgressChanged(object sender, (double progress0To1, string status) e) + { + if (e.status.StartsWith("[[send to terminal]]")) { - printer.Connection.TerminalLog.WriteLine(e.Status); - return; + // strip of the prefix + e.status = e.status.Substring("[[send to terminal]]".Length); + if (sender is RunningTaskDetails details + && details.Owner is PrinterConfig printer) + { + // only write it to the terminal + printer.Connection.TerminalLog.WriteLine(e.status); + return; + } } - if (textWidget.Text != e.Status - && !string.IsNullOrEmpty(e.Status) - && !textWidget.Text.Contains(e.Status, StringComparison.OrdinalIgnoreCase)) + if (textWidget.Text != e.status + && !string.IsNullOrEmpty(e.status) + && !textWidget.Text.Contains(e.status, StringComparison.OrdinalIgnoreCase)) { - textWidget.Text = e.Status.Contains(taskDetails.Title, StringComparison.OrdinalIgnoreCase) ? e.Status : $"{taskDetails.Title} - {e.Status}"; + textWidget.Text = e.status.Contains(taskDetails.Title, StringComparison.OrdinalIgnoreCase) ? e.status : $"{taskDetails.Title} - {e.status}"; } - double ratio = ((int)(e.Progress0To1 * Width)) / this.Width; + double ratio = ((int)(e.progress0To1 * Width)) / this.Width; if (progressBar.RatioComplete != ratio) { progressBar.RatioComplete = ratio; diff --git a/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs b/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs index fb639bd90..6d2d21f5d 100644 --- a/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs +++ b/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,11 +27,6 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using JsonPath; using MatterHackers.Agg; using MatterHackers.Agg.Platform; @@ -44,491 +39,508 @@ using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.SlicerConfiguration; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using static JsonPath.JsonPathContext.ReflectionValueSystem; namespace MatterHackers.MatterControl.PartPreviewWindow { - public class SelectedObjectPanel : FlowLayoutWidget, IContentStore - { - private IObject3D item = new Object3D(); + public class SelectedObjectPanel : FlowLayoutWidget, IContentStore + { + private IObject3D item = new Object3D(); - private readonly ThemeConfig theme; - private readonly ISceneContext sceneContext; - private readonly SectionWidget editorSectionWidget; + private readonly ThemeConfig theme; + private readonly ISceneContext sceneContext; + private readonly SectionWidget editorSectionWidget; - private readonly GuiWidget editorPanel; + private readonly GuiWidget editorPanel; - private readonly string editorTitle = "Properties".Localize(); + private readonly string editorTitle = "Properties".Localize(); - public SelectedObjectPanel(View3DWidget view3DWidget, ISceneContext sceneContext, ThemeConfig theme) - : base(FlowDirection.TopToBottom) - { - this.HAnchor = HAnchor.Stretch; - this.VAnchor = VAnchor.Top | VAnchor.Fit; - this.Padding = 0; - this.theme = theme; - this.sceneContext = sceneContext; + public SelectedObjectPanel(View3DWidget view3DWidget, ISceneContext sceneContext, ThemeConfig theme) + : base(FlowDirection.TopToBottom) + { + this.HAnchor = HAnchor.Stretch; + this.VAnchor = VAnchor.Top | VAnchor.Fit; + this.Padding = 0; + this.theme = theme; + this.sceneContext = sceneContext; - var toolbar = new LeftClipFlowLayoutWidget() - { - BackgroundColor = theme.BackgroundColor, - Padding = theme.ToolbarPadding, - HAnchor = HAnchor.Fit, - VAnchor = VAnchor.Fit - }; + var toolbar = new LeftClipFlowLayoutWidget() + { + BackgroundColor = theme.BackgroundColor, + Padding = theme.ToolbarPadding, + HAnchor = HAnchor.Fit, + VAnchor = VAnchor.Fit + }; - scene = sceneContext.Scene; + scene = sceneContext.Scene; - // put in the container for dynamic actions - primaryActionsPanel = new FlowLayoutWidget() - { - HAnchor = HAnchor.Fit, - VAnchor = VAnchor.Center | VAnchor.Fit - }; + // put in the container for dynamic actions + primaryActionsPanel = new FlowLayoutWidget() + { + HAnchor = HAnchor.Fit, + VAnchor = VAnchor.Center | VAnchor.Fit + }; - toolbar.AddChild(primaryActionsPanel); + toolbar.AddChild(primaryActionsPanel); - // put in a make permanent button - var icon = StaticData.Instance.LoadIcon("apply.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(); - applyButton = new ThemedIconButton(icon, theme) - { - Margin = theme.ButtonSpacing, - ToolTipText = "Apply".Localize(), - Enabled = true - }; - applyButton.Click += (s, e) => - { - if (this.item.CanApply) - { - var item = this.item; - using (new DataConverters3D.SelectionMaintainer(view3DWidget.Scene)) - { - item.Apply(view3DWidget.Scene.UndoBuffer); - } - } - else - { - // try to ungroup it - sceneContext.Scene.UngroupSelection(); - } - }; - toolbar.AddChild(applyButton); - - // put in a remove button - cancelButton = new ThemedIconButton(StaticData.Instance.LoadIcon("cancel.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(), theme) - { - Margin = theme.ButtonSpacing, - ToolTipText = "Cancel".Localize(), - Enabled = scene.SelectedItem != null - }; - cancelButton.Click += (s, e) => - { - var item = this.item; - using (new DataConverters3D.SelectionMaintainer(view3DWidget.Scene)) - { - item.Cancel(view3DWidget.Scene.UndoBuffer); - } - }; - toolbar.AddChild(cancelButton); - - overflowButton = new PopupMenuButton("Action".Localize(), theme) - { - Enabled = scene.SelectedItem != null, - DrawArrow = true, - }; - overflowButton.ToolTipText = "Object Actions".Localize(); - overflowButton.DynamicPopupContent = () => - { - return ApplicationController.Instance.GetModifyMenu(view3DWidget.sceneContext); - }; - toolbar.AddChild(overflowButton); - - editorPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) - { - HAnchor = HAnchor.Stretch, - VAnchor = VAnchor.Fit, - Name = "editorPanel", - }; - - // Wrap editorPanel with scrollable container - var scrollableWidget = new ScrollableWidget(true) - { - HAnchor = HAnchor.Stretch, - VAnchor = VAnchor.Stretch - }; - scrollableWidget.AddChild(editorPanel); - scrollableWidget.ScrollArea.HAnchor = HAnchor.Stretch; - - editorSectionWidget = new SectionWidget(editorTitle, scrollableWidget, theme, toolbar, expandingContent: false, defaultExpansion: true, setContentVAnchor: false) - { - VAnchor = VAnchor.Stretch - }; - this.AddChild(editorSectionWidget); - - this.ContentPanel = editorPanel; - - // Register listeners - scene.SelectionChanged += Scene_SelectionChanged; - } - - public GuiWidget ContentPanel { get; set; } - - private readonly JsonPathContext pathGetter = new JsonPathContext(); - private readonly ThemedIconButton applyButton; - private readonly ThemedIconButton cancelButton; - private readonly PopupMenuButton overflowButton; - private readonly InteractiveScene scene; - private readonly FlowLayoutWidget primaryActionsPanel; - - public void SetActiveItem(ISceneContext sceneContext) - { - var selectedItem = sceneContext?.Scene?.SelectedItem; - if (this.item == selectedItem) - { - return; - } - - this.item = selectedItem; - editorPanel.CloseChildren(); - - // Allow caller to clean up with passing null for selectedItem - if (item == null) - { - editorSectionWidget.Text = editorTitle; - return; - } - - var selectedItemType = selectedItem.GetType(); - - primaryActionsPanel.RemoveChildren(); - - IEnumerable primaryActions; - - if ((primaryActions = SceneOperations.GetPrimaryOperations(selectedItemType)) == null) - { - primaryActions = new List(); - } - else - { - // Loop over primary actions creating a button for each - foreach (var primaryAction in primaryActions) - { - // TODO: Run visible/enable rules on actions, conditionally add/enable as appropriate - var button = new ThemedIconButton(primaryAction.Icon(theme), theme) - { - // Name = namedAction.Title + " Button", - ToolTipText = primaryAction.Title, - Margin = theme.ButtonSpacing, - BackgroundColor = theme.ToolbarButtonBackground, - HoverColor = theme.ToolbarButtonHover, - MouseDownColor = theme.ToolbarButtonDown, - }; - - button.Click += (s, e) => - { - primaryAction.Action.Invoke(sceneContext); - }; - - primaryActionsPanel.AddChild(button); - } - } - - if (primaryActionsPanel.Children.Any()) - { - // add in a separator from the apply and cancel buttons - primaryActionsPanel.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin)); - } - - editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name; - - HashSet mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType); - - var undoBuffer = sceneContext.Scene.UndoBuffer; - - void GetNextSelectionColor(Action setColor) - { - var scene = sceneContext.Scene; - var startingSelection = scene.SelectedItem; - CancellationTokenSource cancellationToken = null; - - void SelectionChanged(object s, EventArgs e) - { - var selection = scene.SelectedItem; - if (selection != null) - { - setColor?.Invoke(selection.WorldColor()); - scene.SelectionChanged -= SelectionChanged; - cancellationToken?.Cancel(); - scene.SelectedItem = startingSelection; - } - } - - var durationSeconds = 20; - - ApplicationController.Instance.Tasks.Execute("Select an object to copy its color".Localize(), - null, - (progress, cancellationTokenIn) => - { - cancellationToken = cancellationTokenIn; - var time = UiThread.CurrentTimerMs; - var status = new ProgressStatus(); - while (UiThread.CurrentTimerMs < time + durationSeconds * 1000 - && !cancellationToken.IsCancellationRequested) - { - Thread.Sleep(30); - status.Progress0To1 = (UiThread.CurrentTimerMs - time) / 1000.0 / durationSeconds; - progress.Report(status); - } - - scene.SelectionChanged -= SelectionChanged; - return Task.CompletedTask; - }); - - scene.SelectionChanged += SelectionChanged; - } - - if (!(selectedItem.GetType().GetCustomAttributes(typeof(HideMeterialAndColor), true).FirstOrDefault() is HideMeterialAndColor)) - { - var firstDetectedColor = selectedItem.VisibleMeshes()?.FirstOrDefault()?.WorldColor(); - var worldColor = Color.White; - if (firstDetectedColor != null) + // put in a make permanent button + var icon = StaticData.Instance.LoadIcon("apply.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(); + applyButton = new ThemedIconButton(icon, theme) + { + Margin = theme.ButtonSpacing, + ToolTipText = "Apply".Localize(), + Enabled = true + }; + applyButton.Click += (s, e) => + { + if (this.item.CanApply) { - worldColor = firstDetectedColor.Value; - } - - // put in a color edit field - var colorField = new ColorField(theme, worldColor, GetNextSelectionColor, true); - colorField.Initialize(0); - colorField.ValueChanged += (s, e) => - { - if (selectedItem.Color != colorField.Color) - { - if (colorField.Color == Color.Transparent) - { - undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Default)); - } - else - { - undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Solid)); - } - } - }; - - ColorButton holeButton = null; - var solidButton = colorField.Content.Descendants().FirstOrDefault(); - GuiWidget otherContainer = null; - TextWidget otherText = null; - GuiWidget holeContainer = null; - GuiWidget solidContainer = null; - void SetOtherOutputSelection(string text) - { - otherText.Text = text; - otherContainer.Visible = true; - holeContainer.BackgroundOutlineWidth = 0; - holeButton.BackgroundOutlineWidth = 1; - - solidContainer.BackgroundOutlineWidth = 0; - solidButton.BackgroundOutlineWidth = 1; - } - - var scaledButtonSize = 24 * GuiWidget.DeviceScale; - void SetButtonStates() - { - switch (selectedItem.OutputType) + var item = this.item; + using (new SelectionMaintainer(view3DWidget.Scene)) { - case PrintOutputTypes.Hole: - holeContainer.BackgroundOutlineWidth = 1; - holeButton.BackgroundOutlineWidth = 2; - holeButton.BackgroundRadius = scaledButtonSize / 2 - 1; + item.Apply(view3DWidget.Scene.UndoBuffer); + } + } + else + { + // try to ungroup it + sceneContext.Scene.UngroupSelection(); + } + }; + toolbar.AddChild(applyButton); - solidContainer.BackgroundOutlineWidth = 0; - solidButton.BackgroundOutlineWidth = 1; - solidButton.BackgroundRadius = scaledButtonSize / 2; - otherContainer.Visible = false; - break; + // put in a remove button + cancelButton = new ThemedIconButton(StaticData.Instance.LoadIcon("cancel.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(), theme) + { + Margin = theme.ButtonSpacing, + ToolTipText = "Cancel".Localize(), + Enabled = scene.SelectedItem != null + }; + cancelButton.Click += (s, e) => + { + var item = this.item; + using (new SelectionMaintainer(view3DWidget.Scene)) + { + item.Cancel(view3DWidget.Scene.UndoBuffer); + } + }; + toolbar.AddChild(cancelButton); - case PrintOutputTypes.Default: - case PrintOutputTypes.Solid: - holeContainer.BackgroundOutlineWidth = 0; - holeButton.BackgroundOutlineWidth = 1; - holeButton.BackgroundRadius = scaledButtonSize / 2; + overflowButton = new PopupMenuButton("Action".Localize(), theme) + { + Enabled = scene.SelectedItem != null, + DrawArrow = true, + }; + overflowButton.ToolTipText = "Object Actions".Localize(); + overflowButton.DynamicPopupContent = () => + { + return ApplicationController.Instance.GetModifyMenu(view3DWidget.sceneContext); + }; + toolbar.AddChild(overflowButton); - solidContainer.BackgroundOutlineWidth = 1; - solidButton.BackgroundOutlineWidth = 2; - solidButton.BackgroundRadius = scaledButtonSize / 2 - 1; - otherContainer.Visible = false; - break; + editorPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Fit, + Name = "editorPanel", + }; - case PrintOutputTypes.Support: - SetOtherOutputSelection("Support".Localize()); - break; + // Wrap editorPanel with scrollable container + var scrollableWidget = new ScrollableWidget(true) + { + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Stretch + }; + scrollableWidget.AddChild(editorPanel); + scrollableWidget.ScrollArea.HAnchor = HAnchor.Stretch; - case PrintOutputTypes.WipeTower: - SetOtherOutputSelection("Wipe Tower".Localize()); - break; + editorSectionWidget = new SectionWidget(editorTitle, scrollableWidget, theme, toolbar, expandingContent: false, defaultExpansion: true, setContentVAnchor: false) + { + VAnchor = VAnchor.Stretch + }; + this.AddChild(editorSectionWidget); - case PrintOutputTypes.Fuzzy: - SetOtherOutputSelection("Fuzzy".Localize()); - break; + this.ContentPanel = editorPanel; + + // Register listeners + scene.SelectionChanged += Scene_SelectionChanged; + } + + public GuiWidget ContentPanel { get; set; } + + private readonly JsonPathContext pathGetter = new JsonPathContext(); + private readonly ThemedIconButton applyButton; + private readonly ThemedIconButton cancelButton; + private readonly PopupMenuButton overflowButton; + private readonly InteractiveScene scene; + private readonly FlowLayoutWidget primaryActionsPanel; + + public void SetActiveItem(ISceneContext sceneContext) + { + var selectedItem = sceneContext?.Scene?.SelectedItem; + if (this.item == selectedItem) + { + return; + } + + this.item = selectedItem; + editorPanel.CloseChildren(); + + // Allow caller to clean up with passing null for selectedItem + if (item == null) + { + editorSectionWidget.Text = editorTitle; + // show any variables that are on the scene + + // and return before showing a properties pannel for the selected part (no selection) + return; + } + + var selectedItemType = selectedItem.GetType(); + + primaryActionsPanel.RemoveChildren(); + + // Get primary actions for selected item + var primaryOperations = new List(); + + // first check if the item implements IPrimaryOperationsSpecifier + if (selectedItem is IPrimaryOperationsSpecifier primaryOperatinsSpecifier) + { + primaryOperations = primaryOperatinsSpecifier.GetOperations().ToList(); + } + // if not check if it is a 3D object + else if (selectedItemType == typeof(Object3D)) + { + primaryOperations.Add(SceneOperations.ById("Scale")); + primaryOperations.Add(SceneOperations.ById("Mirror")); + } + + // Loop over primary actions creating a button for each + foreach (var primaryAction in primaryOperations) + { + // TODO: Run visible/enable rules on actions, conditionally add/enable as appropriate + var button = new ThemedIconButton(primaryAction.Icon(theme), theme) + { + // Name = namedAction.Title + " Button", + ToolTipText = primaryAction.Title, + Margin = theme.ButtonSpacing, + BackgroundColor = theme.ToolbarButtonBackground, + HoverColor = theme.ToolbarButtonHover, + MouseDownColor = theme.ToolbarButtonDown, + }; + + button.Click += (s, e) => + { + primaryAction.Action.Invoke(sceneContext); + }; + + primaryActionsPanel.AddChild(button); + } + + if (primaryActionsPanel.Children.Any()) + { + // add in a separator from the apply and cancel buttons + primaryActionsPanel.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin)); + } + + editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name; + + HashSet mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType); + + var undoBuffer = sceneContext.Scene.UndoBuffer; + + void GetNextSelectionColor(Action setColor) + { + var scene = sceneContext.Scene; + var startingSelection = scene.SelectedItem; + CancellationTokenSource cancellationToken = null; + + void SelectionChanged(object s, EventArgs e) + { + var selection = scene.SelectedItem; + if (selection != null) + { + setColor?.Invoke(selection.WorldColor()); + scene.SelectionChanged -= SelectionChanged; + cancellationToken?.Cancel(); + scene.SelectedItem = startingSelection; } } - void SetToSolid() - { - // make sure the render mode is set to shaded or outline - switch(sceneContext.ViewState.RenderType) + var durationSeconds = 20; + + ApplicationController.Instance.Tasks.Execute("Select an object to copy its color".Localize(), + null, + (progress, cancellationTokenIn) => { - case RenderOpenGl.RenderTypes.Shaded: - case RenderOpenGl.RenderTypes.Outlines: - case RenderOpenGl.RenderTypes.Polygons: - break; + cancellationToken = cancellationTokenIn; + var time = UiThread.CurrentTimerMs; + while (UiThread.CurrentTimerMs < time + durationSeconds * 1000 + && !cancellationToken.IsCancellationRequested) + { + Thread.Sleep(30); + progress?.Invoke((UiThread.CurrentTimerMs - time) / 1000.0 / durationSeconds, null); + } - default: - // make sure the render mode is set to outline - sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines; - break; - } + scene.SelectionChanged -= SelectionChanged; + return Task.CompletedTask; + }); - var currentOutputType = selectedItem.OutputType; - if (currentOutputType != PrintOutputTypes.Solid && currentOutputType != PrintOutputTypes.Default) - { - undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Solid)); - } + scene.SelectionChanged += SelectionChanged; + } - SetButtonStates(); - Invalidate(); - } - - solidButton.Parent.MouseDown += (s, e) => SetToSolid(); - - var colorRow = new SettingsRow("Output".Localize(), null, colorField.Content, theme) - { - // Special top border style for first item in editor - Border = new BorderDouble(0, 1) - }; - editorPanel.AddChild(colorRow); - - // put in a hole button - holeButton = new ColorButton(Color.DarkGray) - { - Margin = new BorderDouble(5, 0, 11, 0), - Width = scaledButtonSize, - Height = scaledButtonSize, - BackgroundRadius = scaledButtonSize / 2, - BackgroundOutlineWidth = 1, - VAnchor = VAnchor.Center, - DisabledColor = theme.MinimalShade, - BorderColor = theme.TextColor, - ToolTipText = "Convert to Hole".Localize(), - }; - - GuiWidget NewTextContainer(string text) - { - var textWidget = new TextWidget(text.Localize(), pointSize: theme.FontSize10, textColor: theme.TextColor) - { - Margin = new BorderDouble(5, 4, 5, 5), - AutoExpandBoundsToText = true, - }; - - var container = new GuiWidget() - { - Margin = new BorderDouble(5, 0), - VAnchor = VAnchor.Fit | VAnchor.Center, - HAnchor = HAnchor.Fit, - BackgroundRadius = 3, - BackgroundOutlineWidth = 1, - BorderColor = theme.PrimaryAccentColor, - Selectable = true, - }; - - container.AddChild(textWidget); - - return container; - } - - var buttonRow = solidButton.Parents().FirstOrDefault(); - solidContainer = NewTextContainer("Solid"); - buttonRow.AddChild(solidContainer, 0); - - buttonRow.AddChild(holeButton, 0); - holeContainer = NewTextContainer("Hole"); - buttonRow.AddChild(holeContainer, 0); - - otherContainer = NewTextContainer(""); - buttonRow.AddChild(otherContainer, 0); - - otherText = otherContainer.Children.First() as TextWidget; - - void SetToHole() - { - if (selectedItem.OutputType != PrintOutputTypes.Hole) - { - undoBuffer.AddAndDo(new MakeHole(selectedItem)); - } - SetButtonStates(); - Invalidate(); - } - - holeButton.Click += (s, e) => SetToHole(); - holeContainer.Click += (s, e) => SetToHole(); - solidContainer.Click += (s, e) => SetToSolid(); - - SetButtonStates(); - void SelectedItemOutputChanged(object sender, EventArgs e) - { - SetButtonStates(); - } - - selectedItem.Invalidated += SelectedItemOutputChanged; - Closed += (s, e) => selectedItem.Invalidated -= SelectedItemOutputChanged; - - // put in a material edit field - var materialField = new MaterialIndexField(sceneContext.Printer, theme, selectedItem.MaterialIndex); - materialField.Initialize(0); - materialField.ValueChanged += (s, e) => - { - if (selectedItem.MaterialIndex != materialField.MaterialIndex) - { - undoBuffer.AddAndDo(new ChangeMaterial(selectedItem, materialField.MaterialIndex)); - } - }; - - materialField.Content.MouseDown += (s, e) => - { - if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Materials) - { - // make sure the render mode is set to material - sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Materials; - } - }; - - // material row - editorPanel.AddChild(new SettingsRow("Material".Localize(), null, materialField.Content, theme)); + if (!(selectedItem.GetType().GetCustomAttributes(typeof(HideMeterialAndColor), true).FirstOrDefault() is HideMeterialAndColor)) + { + AddMaterialAndColorSelector(sceneContext, selectedItem, undoBuffer); } var rows = new SafeList(); - // put in the normal editor - if (selectedItem is ComponentObject3D componentObject - && componentObject.Finalized) + // put in the normal editor + if (selectedItem is ComponentObject3D componentObject + && componentObject.Finalized) { AddComponentEditor(selectedItem, undoBuffer, rows, componentObject); } else - { - if (item != null - && ApplicationController.Instance.Extensions.GetEditorsForType(item.GetType())?.FirstOrDefault() is IObject3DEditor editor) - { - ShowObjectEditor((editor, item, item.Name), selectedItem); - } - } + { + if (item != null + && ApplicationController.Instance.Extensions.GetEditorsForType(item.GetType())?.FirstOrDefault() is IObject3DEditor editor) + { + ShowObjectEditor((editor, item, item.Name), selectedItem); + } + } + + void AddMaterialAndColorSelector(ISceneContext sceneContext, IObject3D selectedItem, UndoBuffer undoBuffer) + { + var firstDetectedColor = selectedItem.VisibleMeshes()?.FirstOrDefault()?.WorldColor(); + var worldColor = Color.White; + if (firstDetectedColor != null) + { + worldColor = firstDetectedColor.Value; + } + + // put in a color edit field + var colorField = new ColorField(theme, worldColor, GetNextSelectionColor, true); + colorField.Initialize(0); + colorField.ValueChanged += (s, e) => + { + if (selectedItem.Color != colorField.Color) + { + if (colorField.Color == Color.Transparent) + { + undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Default)); + } + else + { + undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Solid)); + } + } + }; + + ColorButton holeButton = null; + var solidButton = colorField.Content.Descendants().FirstOrDefault(); + GuiWidget otherContainer = null; + TextWidget otherText = null; + GuiWidget holeContainer = null; + GuiWidget solidContainer = null; + void SetOtherOutputSelection(string text) + { + otherText.Text = text; + otherContainer.Visible = true; + holeContainer.BackgroundOutlineWidth = 0; + holeButton.BackgroundOutlineWidth = 1; + + solidContainer.BackgroundOutlineWidth = 0; + solidButton.BackgroundOutlineWidth = 1; + } + + var scaledButtonSize = 24 * GuiWidget.DeviceScale; + void SetButtonStates() + { + switch (selectedItem.OutputType) + { + case PrintOutputTypes.Hole: + holeContainer.BackgroundOutlineWidth = 1; + holeButton.BackgroundOutlineWidth = 2; + holeButton.BackgroundRadius = scaledButtonSize / 2 - 1; + + solidContainer.BackgroundOutlineWidth = 0; + solidButton.BackgroundOutlineWidth = 1; + solidButton.BackgroundRadius = scaledButtonSize / 2; + otherContainer.Visible = false; + break; + + case PrintOutputTypes.Default: + case PrintOutputTypes.Solid: + holeContainer.BackgroundOutlineWidth = 0; + holeButton.BackgroundOutlineWidth = 1; + holeButton.BackgroundRadius = scaledButtonSize / 2; + + solidContainer.BackgroundOutlineWidth = 1; + solidButton.BackgroundOutlineWidth = 2; + solidButton.BackgroundRadius = scaledButtonSize / 2 - 1; + otherContainer.Visible = false; + break; + + case PrintOutputTypes.Support: + SetOtherOutputSelection("Support".Localize()); + break; + + case PrintOutputTypes.WipeTower: + SetOtherOutputSelection("Wipe Tower".Localize()); + break; + + case PrintOutputTypes.Fuzzy: + SetOtherOutputSelection("Fuzzy".Localize()); + break; + } + } + + void SetToSolid() + { + // make sure the render mode is set to shaded or outline + switch (sceneContext.ViewState.RenderType) + { + case RenderOpenGl.RenderTypes.Shaded: + case RenderOpenGl.RenderTypes.Outlines: + case RenderOpenGl.RenderTypes.Polygons: + break; + + default: + // make sure the render mode is set to outline + sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines; + break; + } + + var currentOutputType = selectedItem.OutputType; + if (currentOutputType != PrintOutputTypes.Solid && currentOutputType != PrintOutputTypes.Default) + { + undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color, PrintOutputTypes.Solid)); + } + + SetButtonStates(); + Invalidate(); + } + + solidButton.Parent.MouseDown += (s, e) => SetToSolid(); + + var colorRow = new SettingsRow("Output".Localize(), null, colorField.Content, theme) + { + // Special top border style for first item in editor + Border = new BorderDouble(0, 1) + }; + editorPanel.AddChild(colorRow); + + // put in a hole button + holeButton = new ColorButton(Color.DarkGray) + { + Margin = new BorderDouble(5, 0, 11, 0), + Width = scaledButtonSize, + Height = scaledButtonSize, + BackgroundRadius = scaledButtonSize / 2, + BackgroundOutlineWidth = 1, + VAnchor = VAnchor.Center, + DisabledColor = theme.MinimalShade, + BorderColor = theme.TextColor, + ToolTipText = "Convert to Hole".Localize(), + }; + + GuiWidget NewTextContainer(string text) + { + var textWidget = new TextWidget(text.Localize(), pointSize: theme.FontSize10, textColor: theme.TextColor) + { + Margin = new BorderDouble(5, 4, 5, 5), + AutoExpandBoundsToText = true, + }; + + var container = new GuiWidget() + { + Margin = new BorderDouble(5, 0), + VAnchor = VAnchor.Fit | VAnchor.Center, + HAnchor = HAnchor.Fit, + BackgroundRadius = 3, + BackgroundOutlineWidth = 1, + BorderColor = theme.PrimaryAccentColor, + Selectable = true, + }; + + container.AddChild(textWidget); + + return container; + } + + var buttonRow = solidButton.Parents().FirstOrDefault(); + solidContainer = NewTextContainer("Solid"); + buttonRow.AddChild(solidContainer, 0); + + buttonRow.AddChild(holeButton, 0); + holeContainer = NewTextContainer("Hole"); + buttonRow.AddChild(holeContainer, 0); + + otherContainer = NewTextContainer(""); + buttonRow.AddChild(otherContainer, 0); + + otherText = otherContainer.Children.First() as TextWidget; + + void SetToHole() + { + if (selectedItem.OutputType != PrintOutputTypes.Hole) + { + undoBuffer.AddAndDo(new MakeHole(selectedItem)); + } + SetButtonStates(); + Invalidate(); + } + + holeButton.Click += (s, e) => SetToHole(); + holeContainer.Click += (s, e) => SetToHole(); + solidContainer.Click += (s, e) => SetToSolid(); + + SetButtonStates(); + void SelectedItemOutputChanged(object sender, EventArgs e) + { + SetButtonStates(); + } + + selectedItem.Invalidated += SelectedItemOutputChanged; + Closed += (s, e) => selectedItem.Invalidated -= SelectedItemOutputChanged; + + // put in a material edit field + var materialField = new MaterialIndexField(sceneContext.Printer, theme, selectedItem.MaterialIndex); + materialField.Initialize(0); + materialField.ValueChanged += (s, e) => + { + if (selectedItem.MaterialIndex != materialField.MaterialIndex) + { + undoBuffer.AddAndDo(new ChangeMaterial(selectedItem, materialField.MaterialIndex)); + } + }; + + materialField.Content.MouseDown += (s, e) => + { + if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Materials) + { + // make sure the render mode is set to material + sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Materials; + } + }; + + // material row + editorPanel.AddChild(new SettingsRow("Material".Localize(), null, materialField.Content, theme)); + } } private void AddComponentEditor(IObject3D selectedItem, UndoBuffer undoBuffer, SafeList rows, ComponentObject3D componentObject) { var context = new PPEContext(); PublicPropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme); - var editorList = componentObject.SurfacedEditors; + var editorList = componentObject.SurfacedEditors; for (var editorIndex = 0; editorIndex < editorList.Count; editorIndex++) { // if it is a reference to a sheet cell @@ -601,8 +613,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; field.Initialize(0); field.SetValue(cellData, false); - field.ClearUndoHistory(); - field.Content.HAnchor = HAnchor.Stretch; + field.ClearUndoHistory(); + field.Content.HAnchor = HAnchor.Stretch; var doOrUndoing = false; field.ValueChanged += (s, e) => @@ -614,23 +626,22 @@ namespace MatterHackers.MatterControl.PartPreviewWindow undoBuffer.AddAndDo(new UndoRedoActions(() => { doOrUndoing = true; - editorList[editorIndex] = "!" + cellId + "," + oldValue; - var expression = new StringOrExpression(oldValue); - cell.Expression = expression.Value(componentObject).ToString(); - componentObject.Invalidate(InvalidateType.SheetUpdated); + editorList[editorIndex] = "!" + cellId + "," + oldValue; + var expression = new StringOrExpression(oldValue); + cell.Expression = expression.Value(componentObject).ToString(); + componentObject.Invalidate(InvalidateType.SheetUpdated); doOrUndoing = false; }, () => { doOrUndoing = true; - editorList[editorIndex] = "!" + cellId + "," + newValue; - var expression = new StringOrExpression(newValue); - cell.Expression = expression.Value(componentObject).ToString(); - componentObject.Invalidate(InvalidateType.SheetUpdated); - doOrUndoing = false; + editorList[editorIndex] = "!" + cellId + "," + newValue; + var expression = new StringOrExpression(newValue); + cell.Expression = expression.Value(componentObject).ToString(); + componentObject.Invalidate(InvalidateType.SheetUpdated); + doOrUndoing = false; })); } - }; var row = new SettingsRow(cell.Name == null ? cellId : cell.Name.Replace("_", " "), null, field.Content, theme); @@ -640,93 +651,93 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } private class OperationButton : ThemedTextButton - { - private readonly SceneOperation sceneOperation; - private readonly ISceneContext sceneContext; + { + private readonly SceneOperation sceneOperation; + private readonly ISceneContext sceneContext; - public OperationButton(SceneOperation sceneOperation, ISceneContext sceneContext, ThemeConfig theme) - : base(sceneOperation.Title, theme) - { - this.sceneOperation = sceneOperation; - this.sceneContext = sceneContext; - } + public OperationButton(SceneOperation sceneOperation, ISceneContext sceneContext, ThemeConfig theme) + : base(sceneOperation.Title, theme) + { + this.sceneOperation = sceneOperation; + this.sceneContext = sceneContext; + } - public void EnsureAvailablity() - { - this.Enabled = sceneOperation.IsEnabled?.Invoke(sceneContext) != false; - } - } + public void EnsureAvailablity() + { + this.Enabled = sceneOperation.IsEnabled?.Invoke(sceneContext) != false; + } + } - private void ShowObjectEditor((IObject3DEditor editor, IObject3D item, string displayName) scopeItem, IObject3D rootSelection) - { - var selectedItem = scopeItem.item; + private void ShowObjectEditor((IObject3DEditor editor, IObject3D item, string displayName) scopeItem, IObject3D rootSelection) + { + var selectedItem = scopeItem.item; - var editorWidget = scopeItem.editor.Create(selectedItem, sceneContext.Scene.UndoBuffer, theme); - editorWidget.HAnchor = HAnchor.Stretch; - editorWidget.VAnchor = VAnchor.Fit; + var editorWidget = scopeItem.editor.Create(selectedItem, sceneContext.Scene.UndoBuffer, theme); + editorWidget.HAnchor = HAnchor.Stretch; + editorWidget.VAnchor = VAnchor.Fit; - if (scopeItem.item != rootSelection - && scopeItem.editor is PublicPropertyEditor) - { - editorWidget.Padding = new BorderDouble(10, 10, 10, 0); + if (scopeItem.item != rootSelection + && scopeItem.editor is PublicPropertyEditor) + { + editorWidget.Padding = new BorderDouble(10, 10, 10, 0); - // EditOutline section - var sectionWidget = new SectionWidget( - scopeItem.displayName ?? "Unknown", - editorWidget, - theme); + // EditOutline section + var sectionWidget = new SectionWidget( + scopeItem.displayName ?? "Unknown", + editorWidget, + theme); - theme.ApplyBoxStyle(sectionWidget, margin: 0); + theme.ApplyBoxStyle(sectionWidget, margin: 0); - editorWidget = sectionWidget; - } - else - { - editorWidget.Padding = 0; - } + editorWidget = sectionWidget; + } + else + { + editorWidget.Padding = 0; + } - editorPanel.AddChild(editorWidget); - } + editorPanel.AddChild(editorWidget); + } - public Task Save(ILibraryItem item, IObject3D content) - { - this.item.Parent.Children.Modify(children => - { - children.Remove(this.item); - children.Add(content); - }); + public Task Save(ILibraryItem item, IObject3D content) + { + this.item.Parent.Children.Modify(children => + { + children.Remove(this.item); + children.Add(content); + }); - return null; - } + return null; + } - public override void OnClosed(EventArgs e) - { - // Unregister listeners - scene.SelectionChanged -= Scene_SelectionChanged; + public override void OnClosed(EventArgs e) + { + // Unregister listeners + scene.SelectionChanged -= Scene_SelectionChanged; - base.OnClosed(e); - } + base.OnClosed(e); + } - private void Scene_SelectionChanged(object sender, EventArgs e) - { - if (editorPanel.Children.FirstOrDefault()?.DescendantsAndSelf().FirstOrDefault() is SectionWidget firstSectionWidget) - { - firstSectionWidget.Margin = firstSectionWidget.Margin.Clone(top: 0); - } + private void Scene_SelectionChanged(object sender, EventArgs e) + { + if (editorPanel.Children.FirstOrDefault()?.DescendantsAndSelf().FirstOrDefault() is SectionWidget firstSectionWidget) + { + firstSectionWidget.Margin = firstSectionWidget.Margin.Clone(top: 0); + } - var selectedItem = scene.SelectedItem; + var selectedItem = scene.SelectedItem; - applyButton.Enabled = selectedItem != null - && (selectedItem is GroupObject3D - || (selectedItem.GetType() == typeof(Object3D) && selectedItem.Children.Any()) - || selectedItem.CanApply); - cancelButton.Enabled = selectedItem != null; - overflowButton.Enabled = selectedItem != null; - if (selectedItem == null) - { - primaryActionsPanel.RemoveChildren(); - } - } + applyButton.Enabled = selectedItem != null + && (selectedItem is GroupObject3D + || (selectedItem.GetType() == typeof(Object3D) && selectedItem.Children.Any()) + || selectedItem.CanApply); + cancelButton.Enabled = selectedItem != null; + overflowButton.Enabled = selectedItem != null; + if (selectedItem == null) + { + primaryActionsPanel.RemoveChildren(); + } + } public void Dispose() { diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs index 7df4d37bb..4c406c6d7 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs @@ -44,141 +44,140 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [ShowUpdateButton] - public class CombineObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread - { + [ShowUpdateButton] + public class CombineObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread + { private CancellationTokenSource cancellationToken; public CombineObject3D_2() - { - Name = "Combine"; - } + { + Name = "Combine"; + } #if DEBUG - public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; + public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public IplicitSurfaceMethod MeshAnalysis { get; set; } + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public IplicitSurfaceMethod MeshAnalysis { get; set; } - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #else private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; private IplicitSurfaceMethod MeshAnalysis { get; set; } private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #endif - public bool IsBuilding => this.cancellationToken != null; + public bool IsBuilding => this.cancellationToken != null; - public static void CheckManifoldData(CombineObject3D_2 item, IObject3D result) - { - bool IsManifold(Mesh mesh) - { - var meshEdgeList = mesh.NewMeshEdges(); + public static void CheckManifoldData(CombineObject3D_2 item, IObject3D result) + { + bool IsManifold(Mesh mesh) + { + var meshEdgeList = mesh.NewMeshEdges(); - foreach (var meshEdge in meshEdgeList) - { - if (meshEdge.Faces.Count() != 2) - { - return false; - } - } + foreach (var meshEdge in meshEdgeList) + { + if (meshEdge.Faces.Count() != 2) + { + return false; + } + } - return true; - } + return true; + } - if (!IsManifold(result.Mesh)) - { - // create a new combine of a and b and add it to the root - var combine = new CombineObject3D_2(); + if (!IsManifold(result.Mesh)) + { + // create a new combine of a and b and add it to the root + var combine = new CombineObject3D_2(); - var participants = item.SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(item.SourceContainer) != PrintOutputTypes.Hole); - // all participants are manifold - foreach (var participant in participants) - { - combine.SourceContainer.Children.Add(new Object3D() - { - Mesh = participant.Mesh.Copy(new CancellationToken()), - Matrix = participant.Matrix - }); - } + var participants = item.SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(item.SourceContainer) != PrintOutputTypes.Hole); + // all participants are manifold + foreach (var participant in participants) + { + combine.SourceContainer.Children.Add(new Object3D() + { + Mesh = participant.Mesh.Copy(new CancellationToken()), + Matrix = participant.Matrix + }); + } - var scene = result.Parents().Last(); - scene.Children.Add(combine); - } - } + var scene = result.Parents().Last(); + scene.Children.Add(combine); + } + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); return ApplicationController.Instance.Tasks.Execute( - "Combine".Localize(), - null, - (reporter, cancellationTokenSource) => - { - this.cancellationToken = cancellationTokenSource; - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + "Combine".Localize(), + null, + (reporter, cancellationTokenSource) => + { + this.cancellationToken = cancellationTokenSource; + reporter?.Invoke(0, null); - try - { - Combine(cancellationTokenSource.Token, reporter); + try + { + Combine(cancellationTokenSource.Token, reporter); - if (cancellationToken.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) { - // the combine was canceled set our children to the source object children - SourceContainer.Visible = true; - RemoveAllButSource(); - Children.Modify((list) => - { - foreach (var child in SourceContainer.Children) - { - list.Add(child); - } - }); + // the combine was canceled set our children to the source object children + SourceContainer.Visible = true; + RemoveAllButSource(); + Children.Modify((list) => + { + foreach (var child in SourceContainer.Children) + { + list.Add(child); + } + }); - SourceContainer.Visible = false; - } - } - catch - { - } + SourceContainer.Visible = false; + } + } + catch + { + } - if (!NameOverriden) - { - Name = NameFromChildren(); - NameOverriden = false; - } + if (!NameOverriden) + { + Name = NameFromChildren(); + NameOverriden = false; + } - this.cancellationToken = null; - UiThread.RunOnIdle(() => - { + this.cancellationToken = null; + UiThread.RunOnIdle(() => + { rebuildLocks.Dispose(); this.CancelAllParentBuilding(); Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + }); - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); + } - public void Combine() - { - Combine(CancellationToken.None, null); - } + public void Combine() + { + Combine(CancellationToken.None, null); + } public override string NameFromChildren() { - return CalculateName(SourceContainer.Children, " + "); - } + return CalculateName(SourceContainer.Children, " + "); + } - private void Combine(CancellationToken cancellationToken, IProgress reporter) + private void Combine(CancellationToken cancellationToken, Action reporter) { SourceContainer.Visible = true; RemoveAllButSource(); @@ -238,27 +237,27 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix)); this.Children.Add(resultsItem); #if DEBUG - //resultsItem.Mesh.MergeVertices(.01); - //resultsItem.Mesh.CleanAndMerge(); - //CheckManifoldData(this, resultsItem); + //resultsItem.Mesh.MergeVertices(.01); + //resultsItem.Mesh.CleanAndMerge(); + //CheckManifoldData(this, resultsItem); #endif - SourceContainer.Visible = false; + SourceContainer.Visible = false; } public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); - } + { + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); + } public void CancelBuild() { - var threadSafe = this.cancellationToken; - if(threadSafe != null) + var threadSafe = this.cancellationToken; + if (threadSafe != null) { - threadSafe.Cancel(); + threadSafe.Cancel(); } } } diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/IntersectionObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/IntersectionObject3D_2.cs index 5c0e3f2ea..cde994799 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/IntersectionObject3D_2.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/IntersectionObject3D_2.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -42,25 +42,25 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [ShowUpdateButton] - public class IntersectionObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier - { - public IntersectionObject3D_2() - { - Name = "Intersection"; - } + [ShowUpdateButton] + public class IntersectionObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier + { + public IntersectionObject3D_2() + { + Name = "Intersection"; + } #if DEBUG - public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; + public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public IplicitSurfaceMethod MeshAnalysis { get; set; } + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public IplicitSurfaceMethod MeshAnalysis { get; set; } - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #else private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; @@ -68,90 +68,88 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #endif - private CancellationTokenSource cancellationToken; + private CancellationTokenSource cancellationToken; - public bool IsBuilding => this.cancellationToken != null; + public bool IsBuilding => this.cancellationToken != null; - public void CancelBuild() - { - var threadSafe = this.cancellationToken; - if (threadSafe != null) - { - threadSafe.Cancel(); - } - } + public void CancelBuild() + { + var threadSafe = this.cancellationToken; + if (threadSafe != null) + { + threadSafe.Cancel(); + } + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); - return ApplicationController.Instance.Tasks.Execute( - "Intersection".Localize(), - null, - (reporter, cancellationTokenSource) => - { - this.cancellationToken = cancellationTokenSource as CancellationTokenSource; - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + return ApplicationController.Instance.Tasks.Execute( + "Intersection".Localize(), + null, + (reporter, cancellationTokenSource) => + { + this.cancellationToken = cancellationTokenSource as CancellationTokenSource; - try - { - Intersect(cancellationTokenSource.Token, reporter); - } - catch - { - } + try + { + Intersect(cancellationTokenSource.Token, reporter); + } + catch + { + } - if (!NameOverriden) - { - Name = NameFromChildren(); - NameOverriden = false; - } + if (!NameOverriden) + { + Name = NameFromChildren(); + NameOverriden = false; + } - this.cancellationToken = null; - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + this.cancellationToken = null; + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); + } - public override string NameFromChildren() - { - return CalculateName(SourceContainer.Children, " & "); - } + public override string NameFromChildren() + { + return CalculateName(SourceContainer.Children, " & "); + } - public void Intersect() - { - Intersect(CancellationToken.None, null); - } + public void Intersect() + { + Intersect(CancellationToken.None, null); + } - private void Intersect(CancellationToken cancellationToken, IProgress reporter) - { - SourceContainer.Visible = true; - RemoveAllButSource(); + private void Intersect(CancellationToken cancellationToken, Action reporter) + { + SourceContainer.Visible = true; + RemoveAllButSource(); - var participants = SourceContainer.VisibleMeshes(); - if (participants.Count() < 2) - { - if (participants.Count() == 1) - { - var newMesh = new Object3D(); - newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All); - newMesh.Mesh = participants.First().Mesh; - this.Children.Add(newMesh); - SourceContainer.Visible = false; - } - return; - } + var participants = SourceContainer.VisibleMeshes(); + if (participants.Count() < 2) + { + if (participants.Count() == 1) + { + var newMesh = new Object3D(); + newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All); + newMesh.Mesh = participants.First().Mesh; + this.Children.Add(newMesh); + SourceContainer.Visible = false; + } + return; + } - var items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer))); + var items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer))); #if false var resultsMesh = BooleanProcessing.DoArray(items, CsgModes.Intersect, @@ -161,69 +159,65 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D reporter, cancellationToken); #else - var totalOperations = items.Count() - 1; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + var totalOperations = items.Count() - 1; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - var progressStatus = new ProgressStatus(); + var resultsMesh = items.First().Item1; + var keepWorldMatrix = items.First().Item2; - var resultsMesh = items.First().Item1; - var keepWorldMatrix = items.First().Item2; + bool first = true; + foreach (var next in items) + { + if (first) + { + first = false; + continue; + } - bool first = true; - foreach (var next in items) - { - if (first) - { - first = false; - continue; - } + resultsMesh = BooleanProcessing.Do(resultsMesh, + keepWorldMatrix, + // other mesh + next.Item1, + next.Item2, + // operation type + CsgModes.Intersect, + Processing, + InputResolution, + OutputResolution, + // reporting + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - resultsMesh = BooleanProcessing.Do(resultsMesh, - keepWorldMatrix, - // other mesh - next.Item1, - next.Item2, - // operation type - CsgModes.Intersect, - Processing, - InputResolution, - OutputResolution, - // reporting - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + // after the first time we get a result the results mesh is in the right coordinate space + keepWorldMatrix = Matrix4X4.Identity; - // after the first time we get a result the results mesh is in the right coordinate space - keepWorldMatrix = Matrix4X4.Identity; - - // report our progress - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } + // report our progress + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, null); + } #endif - if (resultsMesh != null) - { - var resultsItem = new Object3D() - { - Mesh = resultsMesh - }; - resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix)); - this.Children.Add(resultsItem); - SourceContainer.Visible = false; - } - } + if (resultsMesh != null) + { + var resultsItem = new Object3D() + { + Mesh = resultsMesh + }; + resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix)); + this.Children.Add(resultsItem); + SourceContainer.Visible = false; + } + } - public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); - } - } + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); + } + } } diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetEditor.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetEditor.cs index a952da42b..9f82c59c9 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetEditor.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetEditor.cs @@ -181,7 +181,7 @@ namespace MatterHackers.MatterControl.DesignTools // first replace spaces with '_' var name = editSelectedName.Text.Replace(' ', '_'); // next make sure we don't have the exact name already - name = agg_basics.GetNonCollidingName(name, (name) => + name = Util.GetNonCollidingName(name, (name) => { return !existingNames.Contains(name.ToLower()); }, false); diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D.cs index 4c1ee578d..dcf91cafd 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D.cs @@ -48,179 +48,175 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [Obsolete("Use SubtractAndReplaceObject3D_2 instead", false)] - [ShowUpdateButton(SuppressPropertyChangeUpdates = true)] - public class SubtractAndReplaceObject3D : MeshWrapperObject3D, ISelectableChildContainer - { - public SubtractAndReplaceObject3D() - { - Name = "Subtract and Replace"; - } + [Obsolete("Use SubtractAndReplaceObject3D_2 instead", false)] + [ShowUpdateButton(SuppressPropertyChangeUpdates = true)] + public class SubtractAndReplaceObject3D : MeshWrapperObject3D, ISelectableChildContainer + { + public SubtractAndReplaceObject3D() + { + Name = "Subtract and Replace"; + } - public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren(); + public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren(); - public SelectedChildren SelectedChildren => ItemsToSubtract; + public SelectedChildren SelectedChildren => ItemsToSubtract; - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateType); - } - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateType); + } + } - public void SubtractAndReplace() - { - SubtractAndReplace(CancellationToken.None, null); - } + public void SubtractAndReplace() + { + SubtractAndReplace(CancellationToken.None, null); + } - public void SubtractAndReplace(CancellationToken cancellationToken, IProgress reporter) - { - ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); + public void SubtractAndReplace(CancellationToken cancellationToken, Action reporter) + { + ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); - var paintObjects = this.Children - .Where((i) => ItemsToSubtract.Contains(i.ID)) - .SelectMany((h) => h.DescendantsAndSelf()) - .Where((c) => c.OwnerID == this.ID).ToList(); - var keepObjects = this.Children - .Where((i) => !ItemsToSubtract.Contains(i.ID)) - .SelectMany((h) => h.DescendantsAndSelf()) - .Where((c) => c.OwnerID == this.ID).ToList(); + var paintObjects = this.Children + .Where((i) => ItemsToSubtract.Contains(i.ID)) + .SelectMany((h) => h.DescendantsAndSelf()) + .Where((c) => c.OwnerID == this.ID).ToList(); + var keepObjects = this.Children + .Where((i) => !ItemsToSubtract.Contains(i.ID)) + .SelectMany((h) => h.DescendantsAndSelf()) + .Where((c) => c.OwnerID == this.ID).ToList(); - if (paintObjects.Any() - && keepObjects.Any()) - { - var totalOperations = paintObjects.Count * keepObjects.Count; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + if (paintObjects.Any() + && keepObjects.Any()) + { + var totalOperations = paintObjects.Count * keepObjects.Count; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - foreach (var paint in paintObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) - { - var transformedPaint = paint.obj3D.Mesh.Copy(cancellationToken); - transformedPaint.Transform(paint.matrix); - var inverseRemove = paint.matrix.Inverted; - Mesh paintMesh = null; + foreach (var paint in paintObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) + { + var transformedPaint = paint.obj3D.Mesh.Copy(cancellationToken); + transformedPaint.Transform(paint.matrix); + var inverseRemove = paint.matrix.Inverted; + Mesh paintMesh = null; - var progressStatus = new ProgressStatus(); - foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) - { - var transformedKeep = keep.obj3D.Mesh.Copy(cancellationToken); - transformedKeep.Transform(keep.matrix); + foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) + { + var transformedKeep = keep.obj3D.Mesh.Copy(cancellationToken); + transformedKeep.Transform(keep.matrix); - // remove the paint from the original - var subtract = BooleanProcessing.Do(keep.obj3D.Mesh, - keep.matrix, - paint.obj3D.Mesh, - paint.matrix, - CsgModes.Subtract, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); - - var intersect = BooleanProcessing.Do(keep.obj3D.Mesh, - keep.matrix, - paint.obj3D.Mesh, - paint.matrix, - CsgModes.Intersect, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + // remove the paint from the original + var subtract = BooleanProcessing.Do(keep.obj3D.Mesh, + keep.matrix, + paint.obj3D.Mesh, + paint.matrix, + CsgModes.Subtract, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - var inverseKeep = keep.matrix.Inverted; - subtract.Transform(inverseKeep); - using (keep.obj3D.RebuildLock()) - { - keep.obj3D.Mesh = subtract; - } + var intersect = BooleanProcessing.Do(keep.obj3D.Mesh, + keep.matrix, + paint.obj3D.Mesh, + paint.matrix, + CsgModes.Intersect, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - // keep all the intersections together - if (paintMesh == null) - { - paintMesh = intersect; - } - else // union into the current paint - { - paintMesh = BooleanProcessing.Do(paintMesh, - Matrix4X4.Identity, - intersect, - Matrix4X4.Identity, - CsgModes.Subtract, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); - } + var inverseKeep = keep.matrix.Inverted; + subtract.Transform(inverseKeep); + using (keep.obj3D.RebuildLock()) + { + keep.obj3D.Mesh = subtract; + } - if (cancellationToken.IsCancellationRequested) - { - break; - } - } + // keep all the intersections together + if (paintMesh == null) + { + paintMesh = intersect; + } + else // union into the current paint + { + paintMesh = BooleanProcessing.Do(paintMesh, + Matrix4X4.Identity, + intersect, + Matrix4X4.Identity, + CsgModes.Subtract, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); + } - // move the paint mesh back to its original coordinates - paintMesh.Transform(inverseRemove); + if (cancellationToken.IsCancellationRequested) + { + break; + } + } - using (paint.obj3D.RebuildLock()) - { - paint.obj3D.Mesh = paintMesh; - } + // move the paint mesh back to its original coordinates + paintMesh.Transform(inverseRemove); - paint.obj3D.Color = paint.obj3D.WorldColor().WithContrast(keepObjects.First().WorldColor(), 2).ToColor(); - } - } - } + using (paint.obj3D.RebuildLock()) + { + paint.obj3D.Mesh = paintMesh; + } - public override Task Rebuild() - { - var rebuildLocks = this.RebuilLockAll(); + paint.obj3D.Color = paint.obj3D.WorldColor().WithContrast(keepObjects.First().WorldColor(), 2).ToColor(); + } + } + } - // spin up a task to calculate the paint - return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), null, (reporter, cancellationTokenSource) => - { - try - { - SubtractAndReplace(cancellationTokenSource.Token, reporter); - } - catch - { - } + public override Task Rebuild() + { + var rebuildLocks = this.RebuilLockAll(); - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + // spin up a task to calculate the paint + return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), null, (reporter, cancellationTokenSource) => + { + try + { + SubtractAndReplace(cancellationTokenSource.Token, reporter); + } + catch + { + } - return Task.CompletedTask; - }); - } - } + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); + + return Task.CompletedTask; + }); + } + } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs index d990d7508..5b994a79f 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -45,36 +45,36 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [ShowUpdateButton] - public class SubtractAndReplaceObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier - { - public SubtractAndReplaceObject3D_2() - { - Name = "Subtract and Replace"; - } + [ShowUpdateButton] + public class SubtractAndReplaceObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier + { + public SubtractAndReplaceObject3D_2() + { + Name = "Subtract and Replace"; + } - public bool DoEditorDraw(bool isSelected) - { - return isSelected; - } + public bool DoEditorDraw(bool isSelected) + { + return isSelected; + } - [HideFromEditor] - public SelectedChildren ComputedChildren { get; set; } = new SelectedChildren(); + [HideFromEditor] + public SelectedChildren ComputedChildren { get; set; } = new SelectedChildren(); - [DisplayName("Part(s) to Subtract and Replace")] - public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); + [DisplayName("Part(s) to Subtract and Replace")] + public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); #if DEBUG - public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; + public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public IplicitSurfaceMethod MeshAnalysis { get; set; } + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public IplicitSurfaceMethod MeshAnalysis { get; set; } - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #else private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; @@ -82,255 +82,247 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #endif - public void AddEditorTransparents(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) - { - if (layer.Scene.SelectedItem != null - && layer.Scene.SelectedItem == this) - { - var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + public void AddEditorTransparents(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) + { + if (layer.Scene.SelectedItem != null + && layer.Scene.SelectedItem == this) + { + var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); - var removeObjects = parentOfSubtractTargets.Children - .Where(i => SelectedChildren.Contains(i.ID)) - .SelectMany(c => c.VisibleMeshes()) - .ToList(); + var removeObjects = parentOfSubtractTargets.Children + .Where(i => SelectedChildren.Contains(i.ID)) + .SelectMany(c => c.VisibleMeshes()) + .ToList(); - foreach (var item in removeObjects) - { - var color = item.WorldColor(checkOutputType: true); - transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2))); - } + foreach (var item in removeObjects) + { + var color = item.WorldColor(checkOutputType: true); + transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2))); + } - } - } + } + } - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - return; - } + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + return; + } - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return AxisAlignedBoundingBox.Empty(); - } + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return AxisAlignedBoundingBox.Empty(); + } - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateType); - } - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateType); + } + } - private CancellationTokenSource cancellationToken; + private CancellationTokenSource cancellationToken; - public bool IsBuilding => this.cancellationToken != null; + public bool IsBuilding => this.cancellationToken != null; - public void CancelBuild() - { - var threadSafe = this.cancellationToken; - if (threadSafe != null) - { - threadSafe.Cancel(); - } - } + public void CancelBuild() + { + var threadSafe = this.cancellationToken; + if (threadSafe != null) + { + threadSafe.Cancel(); + } + } - public override Task Rebuild() - { - var rebuildLocks = this.RebuilLockAll(); + public override Task Rebuild() + { + var rebuildLocks = this.RebuilLockAll(); - // spin up a task to calculate the paint - return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), - null, - (reporter, cancellationTokenSource) => - { - this.cancellationToken = cancellationTokenSource as CancellationTokenSource; - try - { - SubtractAndReplace(cancellationTokenSource.Token, reporter); - var newComputedChildren = new SelectedChildren(); + // spin up a task to calculate the paint + return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), + null, + (reporter, cancellationTokenSource) => + { + this.cancellationToken = cancellationTokenSource as CancellationTokenSource; + try + { + SubtractAndReplace(cancellationTokenSource.Token, reporter); + var newComputedChildren = new SelectedChildren(); - foreach (var id in SelectedChildren) - { - newComputedChildren.Add(id); - } + foreach (var id in SelectedChildren) + { + newComputedChildren.Add(id); + } - ComputedChildren = newComputedChildren; - } - catch - { - } + ComputedChildren = newComputedChildren; + } + catch + { + } - this.cancellationToken = null; - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + this.cancellationToken = null; + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); + } - public void SubtractAndReplace() - { - SubtractAndReplace(CancellationToken.None, null); - } + public void SubtractAndReplace() + { + SubtractAndReplace(CancellationToken.None, null); + } - private void SubtractAndReplace(CancellationToken cancellationToken, IProgress reporter) - { - SourceContainer.Visible = true; - RemoveAllButSource(); + private void SubtractAndReplace(CancellationToken cancellationToken, Action reporter) + { + SourceContainer.Visible = true; + RemoveAllButSource(); - var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); - if (parentOfPaintTargets.Children.Count() < 2) - { - if (parentOfPaintTargets.Children.Count() == 1) - { - this.Children.Add(SourceContainer.Clone()); - SourceContainer.Visible = false; - } + if (parentOfPaintTargets.Children.Count() < 2) + { + if (parentOfPaintTargets.Children.Count() == 1) + { + this.Children.Add(SourceContainer.Clone()); + SourceContainer.Visible = false; + } - return; - } + return; + } - SubtractObject3D_2.CleanUpSelectedChildrenIDs(this); + SubtractObject3D_2.CleanUpSelectedChildrenIDs(this); - var paintObjects = parentOfPaintTargets.Children - .Where((i) => SelectedChildren - .Contains(i.ID)) - .SelectMany(c => c.VisibleMeshes()) - .ToList(); + var paintObjects = parentOfPaintTargets.Children + .Where((i) => SelectedChildren + .Contains(i.ID)) + .SelectMany(c => c.VisibleMeshes()) + .ToList(); - var keepItems = parentOfPaintTargets.Children - .Where((i) => !SelectedChildren - .Contains(i.ID)); + var keepItems = parentOfPaintTargets.Children + .Where((i) => !SelectedChildren + .Contains(i.ID)); - var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList(); + var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList(); - if (paintObjects.Any() - && keepVisibleItems.Any()) - { - var totalOperations = paintObjects.Count * keepVisibleItems.Count; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + if (paintObjects.Any() + && keepVisibleItems.Any()) + { + var totalOperations = paintObjects.Count * keepVisibleItems.Count; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - var progressStatus = new ProgressStatus - { - Status = "Do CSG" - }; + foreach (var keep in keepVisibleItems) + { + var keepResultsMesh = keep.Mesh; + var keepWorldMatrix = keep.WorldMatrix(SourceContainer); - foreach (var keep in keepVisibleItems) - { - var keepResultsMesh = keep.Mesh; - var keepWorldMatrix = keep.WorldMatrix(SourceContainer); + foreach (var paint in paintObjects) + { + if (cancellationToken.IsCancellationRequested) + { + SourceContainer.Visible = true; + RemoveAllButSource(); + return; + } - foreach (var paint in paintObjects) - { - if (cancellationToken.IsCancellationRequested) - { - SourceContainer.Visible = true; - RemoveAllButSource(); - return; - } + Mesh paintMesh = BooleanProcessing.Do(keepResultsMesh, + keepWorldMatrix, + // paint data + paint.Mesh, + paint.WorldMatrix(SourceContainer), + // operation type + CsgModes.Intersect, + Processing, + InputResolution, + OutputResolution, + // reporting data + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - Mesh paintMesh = BooleanProcessing.Do(keepResultsMesh, - keepWorldMatrix, - // paint data - paint.Mesh, - paint.WorldMatrix(SourceContainer), - // operation type - CsgModes.Intersect, - Processing, - InputResolution, - OutputResolution, - // reporting data - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + keepResultsMesh = BooleanProcessing.Do(keepResultsMesh, + keepWorldMatrix, + // point data + paint.Mesh, + paint.WorldMatrix(SourceContainer), + // operation type + CsgModes.Subtract, + Processing, + InputResolution, + OutputResolution, + // reporting data + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); - keepResultsMesh = BooleanProcessing.Do(keepResultsMesh, - keepWorldMatrix, - // point data - paint.Mesh, - paint.WorldMatrix(SourceContainer), - // operation type - CsgModes.Subtract, - Processing, - InputResolution, - OutputResolution, - // reporting data - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); + // after the first time we get a result the results mesh is in the right coordinate space + keepWorldMatrix = Matrix4X4.Identity; - // after the first time we get a result the results mesh is in the right coordinate space - keepWorldMatrix = Matrix4X4.Identity; + // store our intersection (paint) results mesh + var paintResultsItem = new Object3D() + { + Mesh = paintMesh, + Visible = false, + OwnerID = paint.ID + }; + // copy all the properties but the matrix + paintResultsItem.CopyWorldProperties(paint, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); + // and add it to this + this.Children.Add(paintResultsItem); - // store our intersection (paint) results mesh - var paintResultsItem = new Object3D() - { - Mesh = paintMesh, - Visible = false, - OwnerID = paint.ID - }; - // copy all the properties but the matrix - paintResultsItem.CopyWorldProperties(paint, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); - // and add it to this - this.Children.Add(paintResultsItem); + // report our progress + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, "Do CSG".Localize()); + } - // report our progress - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } + // store our results mesh + var keepResultsItem = new Object3D() + { + Mesh = keepResultsMesh, + Visible = false, + OwnerID = keep.ID + }; + // copy all the properties but the matrix + keepResultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); + // and add it to this + this.Children.Add(keepResultsItem); + } - // store our results mesh - var keepResultsItem = new Object3D() - { - Mesh = keepResultsMesh, - Visible = false, - OwnerID = keep.ID - }; - // copy all the properties but the matrix - keepResultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); - // and add it to this - this.Children.Add(keepResultsItem); - } + foreach (var child in Children) + { + child.Visible = true; + } - foreach (var child in Children) - { - child.Visible = true; - } + SourceContainer.Visible = false; + } + } - SourceContainer.Visible = false; - } - } - - public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); - } - } + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); + } + } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D.cs index 9f9808947..8b031b598 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -46,146 +46,139 @@ using MatterHackers.PolygonMesh.Csg; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [Obsolete("Use SubtractObject3D_2 instead", false)] - [ShowUpdateButton(SuppressPropertyChangeUpdates = true)] - public class SubtractObject3D : MeshWrapperObject3D, ISelectableChildContainer - { - public SubtractObject3D() - { - Name = "Subtract"; - } + [Obsolete("Use SubtractObject3D_2 instead", false)] + [ShowUpdateButton(SuppressPropertyChangeUpdates = true)] + public class SubtractObject3D : MeshWrapperObject3D, ISelectableChildContainer + { + public SubtractObject3D() + { + Name = "Subtract"; + } - public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); + public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } - base.OnInvalidate(invalidateType); - } + base.OnInvalidate(invalidateType); + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + var rebuildLocks = this.RebuilLockAll(); - // spin up a task to remove holes from the objects in the group - return ApplicationController.Instance.Tasks.Execute( - "Subtract".Localize(), - null, - (reporter, cancellationTokenSource) => - { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + // spin up a task to remove holes from the objects in the group + return ApplicationController.Instance.Tasks.Execute( + "Subtract".Localize(), + null, + (reporter, cancellationTokenSource) => + { + try + { + Subtract(cancellationTokenSource.Token, reporter); + } + catch + { + } - try - { - Subtract(cancellationTokenSource.Token, reporter); - } - catch - { - } + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + return Task.CompletedTask; + }); + } - return Task.CompletedTask; - }); - } + public void Subtract() + { + Subtract(CancellationToken.None, null); + } - public void Subtract() - { - Subtract(CancellationToken.None, null); - } + public void Subtract(CancellationToken cancellationToken, Action reporter) + { + ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); - public void Subtract(CancellationToken cancellationToken, IProgress reporter) - { - ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken); + bool ItemInSubtractList(IObject3D item) + { + if (SelectedChildren.Contains(item.ID)) + { + return true; + } - bool ItemInSubtractList(IObject3D item) - { - if (SelectedChildren.Contains(item.ID)) - { - return true; - } + // check if the wrapped item is in the subtract list + if (item.Children.Count > 0 && SelectedChildren.Contains(item.Children.First().ID)) + { + return true; + } - // check if the wrapped item is in the subtract list - if (item.Children.Count > 0 && SelectedChildren.Contains(item.Children.First().ID)) - { - return true; - } + return false; + } - return false; - } + var removeObjects = this.Children + .Where(i => ItemInSubtractList(i)) + .SelectMany(h => h.DescendantsAndSelf()) + .Where(c => c.OwnerID == this.ID).ToList(); + var keepObjects = this.Children + .Where(i => !ItemInSubtractList(i)) + .SelectMany(h => h.DescendantsAndSelf()) + .Where(c => c.OwnerID == this.ID).ToList(); - var removeObjects = this.Children - .Where(i => ItemInSubtractList(i)) - .SelectMany(h => h.DescendantsAndSelf()) - .Where(c => c.OwnerID == this.ID).ToList(); - var keepObjects = this.Children - .Where(i => !ItemInSubtractList(i)) - .SelectMany(h => h.DescendantsAndSelf()) - .Where(c => c.OwnerID == this.ID).ToList(); + if (removeObjects.Any() + && keepObjects.Any()) + { + var totalOperations = removeObjects.Count * keepObjects.Count; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - if (removeObjects.Any() - && keepObjects.Any()) - { - var totalOperations = removeObjects.Count * keepObjects.Count; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + foreach (var remove in removeObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) + { + foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) + { + reporter?.Invoke(0, "Do CSG".Localize()); + var result = BooleanProcessing.Do(keep.obj3D.Mesh, + keep.matrix, + remove.obj3D.Mesh, + remove.matrix, + CsgModes.Subtract, + ProcessingModes.Polygons, + ProcessingResolution._64, + ProcessingResolution._64, + reporter, + amountPerOperation, + ratioCompleted, + cancellationToken); + var inverse = keep.matrix.Inverted; + result.Transform(inverse); - ProgressStatus progressStatus = new ProgressStatus(); - foreach (var remove in removeObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) - { - foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList()) - { - progressStatus.Status = "Do CSG"; - reporter?.Report(progressStatus); - var result = BooleanProcessing.Do(keep.obj3D.Mesh, - keep.matrix, - remove.obj3D.Mesh, - remove.matrix, - CsgModes.Subtract, - ProcessingModes.Polygons, - ProcessingResolution._64, - ProcessingResolution._64, - reporter, - amountPerOperation, - ratioCompleted, - progressStatus, - cancellationToken); - var inverse = keep.matrix.Inverted; - result.Transform(inverse); + using (keep.obj3D.RebuildLock()) + { + keep.obj3D.Mesh = result; + } - using (keep.obj3D.RebuildLock()) - { - keep.obj3D.Mesh = result; - } + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, null); + } - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } - - remove.obj3D.Visible = false; - } - } - } - } + remove.obj3D.Visible = false; + } + } + } + } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs index 521821bec..ff8bfa113 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs @@ -44,29 +44,29 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - [ShowUpdateButton] - public class SubtractObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier, IBuildsOnThread - { - public SubtractObject3D_2() - { - Name = "Subtract"; - NameOverriden = false; - } + [ShowUpdateButton] + public class SubtractObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier, IBuildsOnThread + { + public SubtractObject3D_2() + { + Name = "Subtract"; + NameOverriden = false; + } - [DisplayName("Part(s) to Subtract")] - public virtual SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); + [DisplayName("Part(s) to Subtract")] + public virtual SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); #if DEBUG - public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; + public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public IplicitSurfaceMethod MeshAnalysis { get; set; } + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public IplicitSurfaceMethod MeshAnalysis { get; set; } - [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] - public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #else private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons; private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64; @@ -74,266 +74,264 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64; #endif - public bool RemoveSubtractObjects { get; set; } = true; + public bool RemoveSubtractObjects { get; set; } = true; - public bool DoEditorDraw(bool isSelected) + public bool DoEditorDraw(bool isSelected) { - return isSelected; + return isSelected; } - public void AddEditorTransparents(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) - { - if (layer.Scene.SelectedItem != null - && layer.Scene.SelectedItem == this) - { - var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + public void AddEditorTransparents(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) + { + if (layer.Scene.SelectedItem != null + && layer.Scene.SelectedItem == this) + { + var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); - var removeObjects = parentOfSubtractTargets.Children - .Where(i => SelectedChildren.Contains(i.ID)) - .SelectMany(c => c.VisibleMeshes()) - .ToList(); + var removeObjects = parentOfSubtractTargets.Children + .Where(i => SelectedChildren.Contains(i.ID)) + .SelectMany(c => c.VisibleMeshes()) + .ToList(); - foreach (var item in removeObjects) - { - var color = item.WorldColor(checkOutputType: true); - transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2))); - } + foreach (var item in removeObjects) + { + var color = item.WorldColor(checkOutputType: true); + transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2))); + } - } - } + } + } public override async void WrapSelectedItemAndSelect(InteractiveScene scene) { - // this will ask the subtract to do a rebuild + // this will ask the subtract to do a rebuild base.WrapSelectedItemAndSelect(scene); - - if (SelectedChildren.Count == 0) - { - SelectedChildren.Add(SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Last().ID); - } - } - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - return; - } - - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return AxisAlignedBoundingBox.Empty(); - } - - public override async void OnInvalidate(InvalidateArgs invalidateArgs) - { - if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateArgs.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) - { - await Rebuild(); - } - else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) - { - await Rebuild(); - } - else if(invalidateArgs.InvalidateType.HasFlag(InvalidateType.Name) - && !NameOverriden) - { - Name = NameFromChildren(); - NameOverriden = false; - } - else - { - base.OnInvalidate(invalidateArgs); - } - } - - private CancellationTokenSource cancellationToken; - - public bool IsBuilding => this.cancellationToken != null; - - public void CancelBuild() - { - var threadSafe = this.cancellationToken; - if (threadSafe != null) - { - threadSafe.Cancel(); - } - } - - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); - - var rebuildLocks = this.RebuilLockAll(); - - return ApplicationController.Instance.Tasks.Execute( - "Subtract".Localize(), - null, - (reporter, cancellationTokenSource) => - { - this.cancellationToken = cancellationTokenSource; - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); - - try - { - Subtract(cancellationTokenSource.Token, reporter); - } - catch - { - } - - if (!NameOverriden) - { - Name = NameFromChildren(); - NameOverriden = false; - } - - this.cancellationToken = null; - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); - - return Task.CompletedTask; - }); - } - - public void Subtract() - { - Subtract(CancellationToken.None, null); - } - - private static (IEnumerable, IEnumerable) GetSubtractItems(IObject3D source, SelectedChildren selectedChildren) - { - var parentOfSubtractTargets = source.FirstWithMultipleChildrenDescendantsAndSelf(); - - // if there are 0 results - if (parentOfSubtractTargets.Children.Count() == 0) + if (SelectedChildren.Count == 0) { - return (null, null); + SelectedChildren.Add(SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Last().ID); + } + } + + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + return; + } + + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return AxisAlignedBoundingBox.Empty(); + } + + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateArgs.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateArgs.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) + { + await Rebuild(); + } + else if (Expressions.NeedRebuild(this, invalidateArgs)) + { + await Rebuild(); + } + else if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Name) + && !NameOverriden) + { + Name = NameFromChildren(); + NameOverriden = false; + } + else + { + base.OnInvalidate(invalidateArgs); + } + } + + private CancellationTokenSource cancellationToken; + + public bool IsBuilding => this.cancellationToken != null; + + public void CancelBuild() + { + var threadSafe = this.cancellationToken; + if (threadSafe != null) + { + threadSafe.Cancel(); + } + } + + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + + var rebuildLocks = this.RebuilLockAll(); + + return ApplicationController.Instance.Tasks.Execute( + "Subtract".Localize(), + null, + (reporter, cancellationTokenSource) => + { + this.cancellationToken = cancellationTokenSource; + + try + { + Subtract(cancellationTokenSource.Token, reporter); + } + catch + { + } + + if (!NameOverriden) + { + Name = NameFromChildren(); + NameOverriden = false; + } + + this.cancellationToken = null; + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); + + return Task.CompletedTask; + }); + } + + public void Subtract() + { + Subtract(CancellationToken.None, null); + } + + private static (IEnumerable, IEnumerable) GetSubtractItems(IObject3D source, SelectedChildren selectedChildren) + { + var parentOfSubtractTargets = source.FirstWithMultipleChildrenDescendantsAndSelf(); + + // if there are 0 results + if (parentOfSubtractTargets.Children.Count() == 0) + { + return (null, null); } - // if there is only 1 result (regardless of it being a keep or remove) return it as a keep - if (parentOfSubtractTargets.Children.Count() == 1) - { - return (new IObject3D[] { source }, null); - } - - var removeItems = parentOfSubtractTargets.Children - .Where((i) => selectedChildren - .Contains(i.ID)) - .SelectMany(c => c.VisibleMeshes()); - - var keepItems = parentOfSubtractTargets.Children - .Where((i) => !selectedChildren - .Contains(i.ID)) - .SelectMany(c => c.VisibleMeshes()); - - return (keepItems, removeItems); - } - - private void Subtract(CancellationToken cancellationToken, IProgress reporter) - { - SourceContainer.Visible = true; - RemoveAllButSource(); - - CleanUpSelectedChildrenIDs(this); - - var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren); - - var resultItems = DoSubtract(SourceContainer, - keepItems, - removeItems, - reporter, - cancellationToken, - Processing, - InputResolution, - OutputResolution); - - foreach(var resultsItem in resultItems) + // if there is only 1 result (regardless of it being a keep or remove) return it as a keep + if (parentOfSubtractTargets.Children.Count() == 1) { - this.Children.Add(resultsItem); + return (new IObject3D[] { source }, null); + } - if (!RemoveSubtractObjects) - { - this.Children.Modify((list) => - { - foreach (var item in removeItems) - { - var newObject = new Object3D() - { - Mesh = item.Mesh - }; + var removeItems = parentOfSubtractTargets.Children + .Where((i) => selectedChildren + .Contains(i.ID)) + .SelectMany(c => c.VisibleMeshes()); - newObject.CopyWorldProperties(item, SourceContainer, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Visible)); - list.Add(newObject); - } - }); - } - } + var keepItems = parentOfSubtractTargets.Children + .Where((i) => !selectedChildren + .Contains(i.ID)) + .SelectMany(c => c.VisibleMeshes()); - if (Children.Count == 1) - { - // we only have the source item, leave it visible - } - else // hide the source and show the children - { - bool first = true; - foreach (var child in Children) - { - if (first) - { - // hide the source item - child.Visible = false; - first = false; - } - else - { - child.Visible = true; - } - } - } - } + return (keepItems, removeItems); + } - public static void CleanUpSelectedChildrenIDs(OperationSourceContainerObject3D item) - { - if (item is ISelectableChildContainer selectableChildContainer) - { - var parentOfSubtractTargets = item.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + private void Subtract(CancellationToken cancellationToken, Action reporter) + { + SourceContainer.Visible = true; + RemoveAllButSource(); - var allVisibleIDs = parentOfSubtractTargets.Children.Select(i => i.ID); - // remove any names from SelectedChildren that are not a child we can select - foreach (var id in selectableChildContainer.SelectedChildren.ToArray()) - { - if (!allVisibleIDs.Contains(id)) - { - selectableChildContainer.SelectedChildren.Remove(id); - } - } - } - } + CleanUpSelectedChildrenIDs(this); - public void UpdateControls(PublicPropertyChange change) - { - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); - change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); - } + var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren); + + var resultItems = DoSubtract(SourceContainer, + keepItems, + removeItems, + reporter, + cancellationToken, + Processing, + InputResolution, + OutputResolution); + + foreach (var resultsItem in resultItems) + { + this.Children.Add(resultsItem); + + if (!RemoveSubtractObjects) + { + this.Children.Modify((list) => + { + foreach (var item in removeItems) + { + var newObject = new Object3D() + { + Mesh = item.Mesh + }; + + newObject.CopyWorldProperties(item, SourceContainer, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Visible)); + list.Add(newObject); + } + }); + } + } + + if (Children.Count == 1) + { + // we only have the source item, leave it visible + } + else // hide the source and show the children + { + bool first = true; + foreach (var child in Children) + { + if (first) + { + // hide the source item + child.Visible = false; + first = false; + } + else + { + child.Visible = true; + } + } + } + } + + public static void CleanUpSelectedChildrenIDs(OperationSourceContainerObject3D item) + { + if (item is ISelectableChildContainer selectableChildContainer) + { + var parentOfSubtractTargets = item.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + + var allVisibleIDs = parentOfSubtractTargets.Children.Select(i => i.ID); + // remove any names from SelectedChildren that are not a child we can select + foreach (var id in selectableChildContainer.SelectedChildren.ToArray()) + { + if (!allVisibleIDs.Contains(id)) + { + selectableChildContainer.SelectedChildren.Remove(id); + } + } + } + } + + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons); + change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid); + } public override string NameFromChildren() { - var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren); - return CalculateName(keepItems, ", ", " - ", removeItems, ", "); - } - } + var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren); + return CalculateName(keepItems, ", ", " - ", removeItems, ", "); + } + } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs index c61347e36..02099ef94 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,12 +27,6 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; @@ -41,221 +35,221 @@ using MatterHackers.Localizations; using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.PolygonMesh.Processors; -using MatterHackers.RenderOpenGl; - using MatterHackers.VectorMath; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MatterHackers.MatterControl.PartPreviewWindow.View3D { - public class SubtractPathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider - { - public SubtractPathObject3D() - { - Name = "Subtract"; - } + public class SubtractPathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider, IPrimaryOperationsSpecifier + { + public SubtractPathObject3D() + { + Name = "Subtract"; + } - [DisplayName("Part(s) to Subtract")] - public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); + [DisplayName("Part(s) to Subtract")] + public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren(); - public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) - { - this.DrawPath(); - } + public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) + { + this.DrawPath(); + } - public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) - { - return this.GetWorldspaceAabbOfDrawPath(); - } + public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer) + { + return this.GetWorldspaceAabbOfDrawPath(); + } - public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) - { - object3DControlsLayer.AddControls(ControlTypes.Standard2D); - } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.Standard2D); + } - public override async void OnInvalidate(InvalidateArgs invalidateType) - { - if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) - || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) - && invalidateType.Source != this - && !RebuildLocked) - { - await Rebuild(); - } - else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) - && invalidateType.Source == this) - { - await Rebuild(); - } - else - { - base.OnInvalidate(invalidateType); - } - } + public override async void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix) + || invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh)) + && invalidateType.Source != this + && !RebuildLocked) + { + await Rebuild(); + } + else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties) + && invalidateType.Source == this) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateType); + } + } - public override bool CanApply => true; + public override bool CanApply => true; - public override void Apply(UndoBuffer undoBuffer) - { - this.FlattenToPathObject(undoBuffer); - } + public override void Apply(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } - public override Task Rebuild() - { - this.DebugDepth("Rebuild"); + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); - var rebuildLocks = this.RebuilLockAll(); + var rebuildLocks = this.RebuilLockAll(); - return ApplicationController.Instance.Tasks.Execute( - "Subtract".Localize(), - null, - (reporter, cancellationTokenSource) => - { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); + return ApplicationController.Instance.Tasks.Execute( + "Subtract".Localize(), + null, + (reporter, cancellationTokenSource) => + { + try + { + Subtract(cancellationTokenSource.Token, reporter); + } + catch + { + } - try - { - Subtract(cancellationTokenSource.Token, reporter); - } - catch - { - } + // set the mesh to show the path + if (this.GetVertexSource() != null) + { + var extrudeMesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight); + if (extrudeMesh.Vertices.Count() > 5) + { + this.Mesh = extrudeMesh; + } + else + { + this.Mesh = null; + } + } - // set the mesh to show the path - if (this.GetVertexSource() != null) - { - var extrudeMesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight); - if (extrudeMesh.Vertices.Count() > 5) - { - this.Mesh = extrudeMesh; - } - else - { - this.Mesh = null; - } - } + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); - UiThread.RunOnIdle(() => - { - rebuildLocks.Dispose(); - this.CancelAllParentBuilding(); - Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); - }); + return Task.CompletedTask; + }); + } - return Task.CompletedTask; - }); - } + public void Subtract() + { + Subtract(CancellationToken.None, null); + } - public void Subtract() - { - Subtract(CancellationToken.None, null); - } + private void Subtract(CancellationToken cancellationToken, Action reporter) + { + SourceContainer.Visible = true; + RemoveAllButSource(); - private void Subtract(CancellationToken cancellationToken, IProgress reporter) - { - SourceContainer.Visible = true; - RemoveAllButSource(); + var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); - var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf(); + if (parentOfSubtractTargets.Children.Count() < 2) + { + if (parentOfSubtractTargets.Children.Count() == 1) + { + this.Children.Add(SourceContainer.Clone()); + SourceContainer.Visible = false; + } - if (parentOfSubtractTargets.Children.Count() < 2) - { - if (parentOfSubtractTargets.Children.Count() == 1) - { - this.Children.Add(SourceContainer.Clone()); - SourceContainer.Visible = false; - } + return; + } - return; - } + CleanUpSelectedChildrenNames(this); - CleanUpSelectedChildrenNames(this); + var removeVisibleItems = parentOfSubtractTargets.Children + .Where((i) => SelectedChildren + .Contains(i.ID)) + .SelectMany(c => c.VisiblePaths()) + .ToList(); - var removeVisibleItems = parentOfSubtractTargets.Children - .Where((i) => SelectedChildren - .Contains(i.ID)) - .SelectMany(c => c.VisiblePaths()) - .ToList(); + var keepItems = parentOfSubtractTargets.Children + .Where((i) => !SelectedChildren + .Contains(i.ID)); - var keepItems = parentOfSubtractTargets.Children - .Where((i) => !SelectedChildren - .Contains(i.ID)); + var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePaths()).ToList(); - var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePaths()).ToList(); + if (removeVisibleItems.Any() + && keepVisibleItems.Any()) + { + var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count; + double amountPerOperation = 1.0 / totalOperations; + double ratioCompleted = 0; - if (removeVisibleItems.Any() - && keepVisibleItems.Any()) - { - var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count; - double amountPerOperation = 1.0 / totalOperations; - double ratioCompleted = 0; + bool first = true; + foreach (var keep in keepVisibleItems) + { + var resultsVertexSource = keep.GetVertexSource().Transform(keep.Matrix); - var progressStatus = new ProgressStatus - { - Status = "Do Subtract" - }; + foreach (var remove in removeVisibleItems) + { + resultsVertexSource = resultsVertexSource.MergePaths(remove.GetVertexSource().Transform(remove.Matrix), ClipperLib.ClipType.ctDifference); - bool first = true; - foreach (var keep in keepVisibleItems) - { - var resultsVertexSource = keep.GetVertexSource().Transform(keep.Matrix); + // report our progress + ratioCompleted += amountPerOperation; + reporter?.Invoke(ratioCompleted, "Do Subtract".Localize()); + } - foreach (var remove in removeVisibleItems) - { - resultsVertexSource = resultsVertexSource.MergePaths(remove.GetVertexSource().Transform(remove.Matrix), ClipperLib.ClipType.ctDifference); + if (first) + { + this.VertexStorage = new VertexStorage(resultsVertexSource); + first = false; + } + else + { + this.GetVertexSource().MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion); + } + } - // report our progress - ratioCompleted += amountPerOperation; - progressStatus.Progress0To1 = ratioCompleted; - reporter?.Report(progressStatus); - } + // this.VertexSource = this.VertexSource.Transform(Matrix.Inverted); + first = true; + foreach (var child in Children) + { + if (first) + { + // hide the source item + child.Visible = false; + first = false; + } + else + { + child.Visible = true; + } + } + } + } - if (first) - { - this.VertexStorage = new VertexStorage(resultsVertexSource); - first = false; - } - else - { - this.GetVertexSource().MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion); - } - } + public static void CleanUpSelectedChildrenNames(OperationSourceContainerObject3D item) + { + if (item is ISelectableChildContainer selectableChildContainer) + { + var parentOfSubtractTargets = item.FirstWithMultipleChildrenDescendantsAndSelf(); - // this.VertexSource = this.VertexSource.Transform(Matrix.Inverted); - first = true; - foreach (var child in Children) - { - if (first) - { - // hide the source item - child.Visible = false; - first = false; - } - else - { - child.Visible = true; - } - } - } - } + var allVisibleNames = parentOfSubtractTargets.Children.Select(i => i.ID); + // remove any names from SelectedChildren that are not a child we can select + foreach (var name in selectableChildContainer.SelectedChildren.ToArray()) + { + if (!allVisibleNames.Contains(name)) + { + selectableChildContainer.SelectedChildren.Remove(name); + } + } + } + } - public static void CleanUpSelectedChildrenNames(OperationSourceContainerObject3D item) - { - if (item is ISelectableChildContainer selectableChildContainer) - { - var parentOfSubtractTargets = item.FirstWithMultipleChildrenDescendantsAndSelf(); - - var allVisibleNames = parentOfSubtractTargets.Children.Select(i => i.ID); - // remove any names from SelectedChildren that are not a child we can select - foreach (var name in selectableChildContainer.SelectedChildren.ToArray()) - { - if (!allVisibleNames.Contains(name)) - { - selectableChildContainer.SelectedChildren.Remove(name); - } - } - } - } - } + public IEnumerable GetOperations() + { + return PathObject3D.GetOperations(this.GetType()); + } + } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs b/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs index f1a4ca26d..9bc9bd083 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs @@ -175,7 +175,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var clonedItem = item.Clone(); clonedItem.Translate(xOffset); // make the name unique - var newName = agg_basics.GetNonCollidingName(item.Name, namedItems); + var newName = Util.GetNonCollidingName(item.Name, namedItems); clonedItem.Name = newName; // add it to the scene scene.Children.Add(clonedItem); @@ -192,7 +192,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (!string.IsNullOrWhiteSpace(sourceItem.Name)) { // make the name unique - var newName = agg_basics.GetNonCollidingName(sourceItem.Name, namedItems); + var newName = Util.GetNonCollidingName(sourceItem.Name, namedItems); clonedItem.Name = newName; } @@ -499,24 +499,21 @@ namespace MatterHackers.MatterControl.PartPreviewWindow null, (reporter, cancellationTokenSource) => { - var progressStatus = new ProgressStatus(); - reporter.Report(progressStatus); // clear the selection scene.SelectedItem = null; - progressStatus.Status = "Copy".Localize(); - reporter.Report(progressStatus); + var status = "Copy".Localize(); + reporter?.Invoke(0, status); // try to cut it up into multiple meshes - progressStatus.Status = "Split".Localize(); + status = "Split".Localize(); var cleanMesh = selectedItem.Mesh.Copy(cancellationTokenSource.Token); cleanMesh.MergeVertices(.01); var discreetMeshes = CreateDiscreteMeshes.SplitVolumesIntoMeshes(cleanMesh, cancellationTokenSource.Token, (double progress0To1, string processingState) => { - progressStatus.Progress0To1 = .5 + progress0To1 * .5; - progressStatus.Status = processingState; - reporter.Report(progressStatus); + status = processingState; + reporter?.Invoke(.5 + progress0To1 * .5, status); }); if (cancellationTokenSource.IsCancellationRequested) { diff --git a/MatterControlLib/PartPreviewWindow/View3D/SliceProgressReporter.cs b/MatterControlLib/PartPreviewWindow/View3D/SliceProgressReporter.cs index 262e75441..d2ab02758 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/SliceProgressReporter.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/SliceProgressReporter.cs @@ -33,21 +33,22 @@ using MatterHackers.Agg; namespace MatterHackers.MatterControl.PartPreviewWindow { - public class SliceProgressReporter : IProgress + public class SliceProgressReporter { - private IProgress reporter; + private Action reporter; - public SliceProgressReporter(IProgress reporter) + public SliceProgressReporter(Action reporter) { this.reporter = reporter; } - public void Report(ProgressStatus progressStatus) + public void Report(string status) { double currentValue = 0; double destValue = 10; - string statusText = progressStatus.Status = progressStatus.Status.Trim().TrimEnd('.'); + string statusText = status.Trim().TrimEnd('.'); + var progress = 0.0; if (GCodeFile.GetFirstNumberAfter("", statusText, ref currentValue) && GCodeFile.GetFirstNumberAfter("/", statusText, ref destValue)) @@ -57,16 +58,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow destValue = 1; } - progressStatus.Progress0To1 = currentValue / destValue; - progressStatus.Target = null; + progress = currentValue / destValue; } else { - progressStatus.Status = statusText; - progressStatus.Target = "terminal"; + status = "[[send to terminal]]" + statusText; } - reporter.Report(progressStatus); + reporter?.Invoke(progress, status); } } } \ No newline at end of file diff --git a/MatterControlLib/PrinterCommunication/Drivers/X3G/X3GExport.cs b/MatterControlLib/PrinterCommunication/Drivers/X3G/X3GExport.cs index 567362a1b..2b43b170c 100644 --- a/MatterControlLib/PrinterCommunication/Drivers/X3G/X3GExport.cs +++ b/MatterControlLib/PrinterCommunication/Drivers/X3G/X3GExport.cs @@ -78,7 +78,7 @@ namespace MatterHackers.MatterControl.Plugins.X3GDriver public override bool ExportPossible(ILibraryAsset libraryItem) => true; - public override async Task> Generate(IEnumerable libraryItems, string outputPath, IProgress progress, CancellationToken cancellationToken) + public override async Task> Generate(IEnumerable libraryItems, string outputPath, Action progress, CancellationToken cancellationToken) { string gcodePath = Path.ChangeExtension(outputPath, "_gcode"); diff --git a/MatterControlLib/PrinterCommunication/Io/ValidatePrintLevelingStream.cs b/MatterControlLib/PrinterCommunication/Io/ValidatePrintLevelingStream.cs index cb0b3c06d..ad33b6f5d 100644 --- a/MatterControlLib/PrinterCommunication/Io/ValidatePrintLevelingStream.cs +++ b/MatterControlLib/PrinterCommunication/Io/ValidatePrintLevelingStream.cs @@ -411,21 +411,20 @@ namespace MatterHackers.MatterControl.PrinterCommunication.Io printer, (reporter, cancellationToken) => { - var progressStatus = new ProgressStatus(); - + var status = ""; while (validationRunning) { if (activeProbeIndex == 0) { - progressStatus.Status = "Validating"; + status = "Validating"; } else { - progressStatus.Status = $"Probing point {activeProbeIndex} of {sampledPositions.Count}"; + status = $"Probing point {activeProbeIndex} of {sampledPositions.Count}"; } - progressStatus.Progress0To1 = (activeProbeIndex + 1) / (double)sampledPositions.Count; - reporter.Report(progressStatus); + var progress0To1 = (activeProbeIndex + 1) / (double)sampledPositions.Count; + reporter?.Invoke(progress0To1, status); Thread.Sleep(100); } diff --git a/MatterControlLib/SettingsManagement/OemSettings.cs b/MatterControlLib/SettingsManagement/OemSettings.cs index a4d13d9e0..5e2e274b7 100644 --- a/MatterControlLib/SettingsManagement/OemSettings.cs +++ b/MatterControlLib/SettingsManagement/OemSettings.cs @@ -242,7 +242,6 @@ namespace MatterHackers.MatterControl.SettingsManagement private async Task DownloadMissingProfiles() { - var reportValue = new ProgressStatus(); int index = 0; foreach (string oem in OemProfiles.Keys) { diff --git a/MatterControlLib/SlicerConfiguration/EngineMappingsMatterSlice.cs b/MatterControlLib/SlicerConfiguration/EngineMappingsMatterSlice.cs index d7c704d63..5931f999c 100644 --- a/MatterControlLib/SlicerConfiguration/EngineMappingsMatterSlice.cs +++ b/MatterControlLib/SlicerConfiguration/EngineMappingsMatterSlice.cs @@ -38,6 +38,7 @@ using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; using MatterHackers.MatterControl.DataStorage; using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools.Operations; @@ -269,8 +270,6 @@ namespace MatterHackers.MatterControl.SlicerConfiguration public static List<(Matrix4X4 matrix, string fileName)> GetStlFileLocations(ref string mergeRules, IEnumerable printableItems, PrinterSettings settings) { - var progressStatus = new ProgressStatus(); - Slicer.GetExtrudersUsed(Slicer.ExtrudersUsed, printableItems, settings, true); // TODO: Once graph parsing is added to MatterSlice we can remove and avoid this flattening @@ -349,7 +348,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration return new List<(Matrix4X4 matrix, string fileName)>(); } - public Task Slice(IEnumerable printableItems, PrinterSettings settings, string gcodeFilePath, IProgress reporter, CancellationToken cancellationToken) + public Task Slice(IEnumerable printableItems, PrinterSettings settings, string gcodeFilePath, Action reporter, CancellationToken cancellationToken) { string mergeRules = ""; @@ -367,18 +366,13 @@ namespace MatterHackers.MatterControl.SlicerConfiguration if (stlFileLocations.Count > 0) { - var progressStatus = new ProgressStatus() - { - Status = "Generating Config" - }; - sliceProgressReporter.Report(progressStatus); + sliceProgressReporter.Report("Generating Config".Localize()); string configFilePath = Path.Combine( ApplicationDataStorage.Instance.GCodeOutputPath, string.Format("config_{0}.ini", settings.GetGCodeCacheKey().ToString())); - progressStatus.Status = "Starting slicer"; - sliceProgressReporter.Report(progressStatus); + sliceProgressReporter.Report("Starting slicer".Localize()); if (!File.Exists(gcodeFilePath) || !HasCompletedSuccessfully(gcodeFilePath)) @@ -435,10 +429,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration if (s is string stringValue) { - sliceProgressReporter?.Report(new ProgressStatus() - { - Status = stringValue - }); + sliceProgressReporter?.Report(stringValue); } } @@ -493,10 +484,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration message += "..."; - sliceProgressReporter?.Report(new ProgressStatus() - { - Status = message - }); + sliceProgressReporter?.Report(message); } }; diff --git a/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs b/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs index 6f186bdc8..54a86eb85 100644 --- a/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs +++ b/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs @@ -623,7 +623,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration var printerInfo = new PrinterInfo { - Name = agg_basics.GetNonCollidingName(fileName, existingPrinterNames), + Name = Util.GetNonCollidingName(fileName, existingPrinterNames), ID = Guid.NewGuid().ToString(), Make = "Other", Model = "Other", diff --git a/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs b/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs index 3065faadc..8b6b7850f 100644 --- a/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs +++ b/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs @@ -105,7 +105,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration duplicateButton.Click += (s, e) => { string sanitizedName = numberMatch.Replace(inlineNameEdit.Text, "").Trim(); - string newProfileName = agg_basics.GetNonCollidingName(sanitizedName, new HashSet(presetsContext.PresetLayers.Select(preset => preset.ValueOrDefault(SettingsKey.layer_name)))); + string newProfileName = Util.GetNonCollidingName(sanitizedName, new HashSet(presetsContext.PresetLayers.Select(preset => preset.ValueOrDefault(SettingsKey.layer_name)))); var clonedLayer = presetsContext.PersistenceLayer.Clone(); clonedLayer.Name = newProfileName; diff --git a/MatterControlLib/SlicerConfiguration/Slicer.cs b/MatterControlLib/SlicerConfiguration/Slicer.cs index cb1594aa0..09e49af90 100644 --- a/MatterControlLib/SlicerConfiguration/Slicer.cs +++ b/MatterControlLib/SlicerConfiguration/Slicer.cs @@ -150,7 +150,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration return itemsThisExtruder; } - public static Task SliceItem(IObject3D object3D, string gcodeFilePath, PrinterConfig printer, IProgress progressReporter, CancellationToken cancellationToken) + public static Task SliceItem(IObject3D object3D, string gcodeFilePath, PrinterConfig printer, Action progressReporter, CancellationToken cancellationToken) { return printer.Settings.Slicer.Slice(printer.PrintableItems(object3D), printer.Settings, gcodeFilePath, progressReporter, cancellationToken); } diff --git a/Plugins/MatterControl.PartSheet/PartsSheetCreator.cs b/Plugins/MatterControl.PartSheet/PartsSheetCreator.cs index 669ca1151..b70275af7 100644 --- a/Plugins/MatterControl.PartSheet/PartsSheetCreator.cs +++ b/Plugins/MatterControl.PartSheet/PartsSheetCreator.cs @@ -1,5 +1,5 @@ /* -Copyright (c) 2017, Lars Brubaker, John Lewin +Copyright (c) 2023, Lars Brubaker, Kevin Pope, John Lewin All rights reserved. Redistribution and use in source and binary forms, with or without @@ -27,12 +27,6 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.Font; using MatterHackers.Agg.Image; @@ -40,298 +34,309 @@ using MatterHackers.Agg.Platform; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; -using MatterHackers.MatterControl.DataStorage; using MatterHackers.MatterControl.Library; using MatterHackers.VectorMath; using PdfSharp.Drawing; using PdfSharp.Pdf; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MatterHackers.MatterControl.Plugins { - public class PartsSheet - { - internal class PartImage - { - internal double xOffset = 0; - internal bool wasDrawn = false; - internal ImageBuffer image; + public class PartsSheet + { + private const double inchesPerMm = 0.0393701; - public PartImage(ImageBuffer imageOfPart) - { - this.image = imageOfPart; - } - } + private static bool currentlySaving = false; - private string pathAndFileToSaveTo; + private List itemSource; - public class FileNameAndPresentationName - { - public string fileName; - public string presentationName; + private List partImagesToPrint = new List(); - public FileNameAndPresentationName(string fileName, string presentationName) - { - this.fileName = fileName; - this.presentationName = presentationName; - } - } + private string pathAndFileToSaveTo; + private bool openAfterSave; - private List itemSource; - private List partImagesToPrint = new List(); - private const double inchesPerMm = 0.0393701; + public PartsSheet(IEnumerable itemSource, string pathAndFileToSaveTo, bool openAfterSave = true) + { + this.pathAndFileToSaveTo = pathAndFileToSaveTo; + this.openAfterSave = openAfterSave; + SheetDpi = 300; + SheetSizeInches = new Vector2(8.5, 11); - private static bool currentlySaving = false; + this.itemSource = itemSource.ToList(); + } - public Vector2 SheetSizeMM { get; set; } + public BorderDouble PageMarginMM { get; } = new BorderDouble(10, 25, 10, 5); - public Vector2 SheetSizeInches - { - get { return SheetSizeMM * inchesPerMm; } - set { SheetSizeMM = value / inchesPerMm; } - } + public BorderDouble PageMarginPixels => PageMarginMM * PixelsPerMM; - public Vector2 SheetSizePixels => SheetSizeMM * PixelsPerMM; + public double PartMarginMM { get; } = 2; - public double PixelsPerMM => inchesPerMm * SheetDpi; + public double PartMarginPixels => PartMarginMM * PixelsPerMM; - public BorderDouble PageMarginMM { get; } = new BorderDouble(10, 25, 10, 5); + public double PartPaddingMM { get; } = 2; - public BorderDouble PageMarginPixels => PageMarginMM * PixelsPerMM; + public double PartPaddingPixels => PartPaddingMM * PixelsPerMM; - public double PartMarginMM { get; } = 2; + public double PixelsPerMM => inchesPerMm * SheetDpi; - public double PartMarginPixels => PartMarginMM * PixelsPerMM; + public int SheetDpi { get; set; } - public double PartPaddingMM { get; } = 2; + public Vector2 SheetSizeInches + { + get { return SheetSizeMM * inchesPerMm; } + set { SheetSizeMM = value / inchesPerMm; } + } - public double PartPaddingPixels => PartPaddingMM * PixelsPerMM; + public Vector2 SheetSizeMM { get; set; } - public int SheetDpi { get; set; } + public Vector2 SheetSizePixels => SheetSizeMM * PixelsPerMM; - public PartsSheet(IEnumerable itemSource, string pathAndFileToSaveTo) - { - this.pathAndFileToSaveTo = pathAndFileToSaveTo; - SheetDpi = 300; - SheetSizeInches = new Vector2(8.5, 11); + public static bool IsSaving() + { + return currentlySaving; + } - this.itemSource = itemSource.ToList(); - } + private async Task ExportTask(Action reporter, CancellationTokenSource cancellationToken) + { + var processCount = 0.0; + currentlySaving = true; + // first create images for all the parts + foreach (var item in itemSource) + { + reporter?.Invoke(0, item.Name); - public Task SaveSheets() - { - return ApplicationController.Instance.Tasks.Execute( - "Export Part Sheet".Localize(), - null, - async (reporter, cancelationToken) => - { - var progressStatus = new ProgressStatus(); + var xxx = itemSource.Count(); + var yyy = itemSource.FirstOrDefault()?.Name; - var processCount = 0.0; - currentlySaving = true; - // first create images for all the parts - foreach (var item in itemSource) - { - progressStatus.Status = item.Name; - reporter.Report(progressStatus); + var object3D = await item.CreateContent(); - var xxx = itemSource.Count(); - var yyy = itemSource.FirstOrDefault()?.Name; + var loadedMeshGroups = object3D.VisibleMeshes().ToList(); + if (loadedMeshGroups?.Count > 0) + { + AxisAlignedBoundingBox aabb = loadedMeshGroups[0].Mesh.GetAxisAlignedBoundingBox(loadedMeshGroups[0].WorldMatrix()); - var object3D = await item.CreateContent(); + for (int i = 1; i < loadedMeshGroups.Count; i++) + { + aabb = AxisAlignedBoundingBox.Union(aabb, loadedMeshGroups[i].Mesh.GetAxisAlignedBoundingBox(loadedMeshGroups[i].WorldMatrix())); + } - var loadedMeshGroups = object3D.VisibleMeshes().ToList(); - if (loadedMeshGroups?.Count > 0) - { - AxisAlignedBoundingBox aabb = loadedMeshGroups[0].Mesh.GetAxisAlignedBoundingBox(loadedMeshGroups[0].WorldMatrix()); + RectangleDouble bounds2D = new RectangleDouble(aabb.MinXYZ.X, aabb.MinXYZ.Y, aabb.MaxXYZ.X, aabb.MaxXYZ.Y); + double widthInMM = bounds2D.Width + PartMarginMM * 2; + double textSpaceMM = 5; + double heightMM = textSpaceMM + bounds2D.Height + PartMarginMM * 2; - for (int i = 1; i < loadedMeshGroups.Count; i++) - { - aabb = AxisAlignedBoundingBox.Union(aabb, loadedMeshGroups[i].Mesh.GetAxisAlignedBoundingBox(loadedMeshGroups[i].WorldMatrix())); - } + TypeFacePrinter typeFacePrinter = new TypeFacePrinter(item.Name, 28, Vector2.Zero, Justification.Center, Baseline.BoundsCenter); + double sizeOfNameX = typeFacePrinter.GetSize().X + PartMarginPixels * 2; + Vector2 sizeOfRender = new Vector2(widthInMM * PixelsPerMM, heightMM * PixelsPerMM); - RectangleDouble bounds2D = new RectangleDouble(aabb.MinXYZ.X, aabb.MinXYZ.Y, aabb.MaxXYZ.X, aabb.MaxXYZ.Y); - double widthInMM = bounds2D.Width + PartMarginMM * 2; - double textSpaceMM = 5; - double heightMM = textSpaceMM + bounds2D.Height + PartMarginMM * 2; + ImageBuffer imageOfPart = new ImageBuffer((int)(Math.Max(sizeOfNameX, sizeOfRender.X)), (int)(sizeOfRender.Y)); + typeFacePrinter.Origin = new Vector2(imageOfPart.Width / 2, (textSpaceMM / 2) * PixelsPerMM); - TypeFacePrinter typeFacePrinter = new TypeFacePrinter(item.Name, 28, Vector2.Zero, Justification.Center, Baseline.BoundsCenter); - double sizeOfNameX = typeFacePrinter.GetSize().X + PartMarginPixels * 2; - Vector2 sizeOfRender = new Vector2(widthInMM * PixelsPerMM, heightMM * PixelsPerMM); + Graphics2D partGraphics2D = imageOfPart.NewGraphics2D(); - ImageBuffer imageOfPart = new ImageBuffer((int)(Math.Max(sizeOfNameX, sizeOfRender.X)), (int)(sizeOfRender.Y)); - typeFacePrinter.Origin = new Vector2(imageOfPart.Width / 2, (textSpaceMM / 2) * PixelsPerMM); + RectangleDouble rectBounds = new RectangleDouble(0, 0, imageOfPart.Width, imageOfPart.Height); + double strokeWidth = .5 * PixelsPerMM; + rectBounds.Inflate(-strokeWidth / 2); + RoundedRect rect = new RoundedRect(rectBounds, PartMarginMM * PixelsPerMM); + partGraphics2D.Render(rect, Color.LightGray); + Stroke rectOutline = new Stroke(rect, strokeWidth); + partGraphics2D.Render(rectOutline, Color.DarkGray); - Graphics2D partGraphics2D = imageOfPart.NewGraphics2D(); + foreach (var meshGroup in loadedMeshGroups) + { + PolygonMesh.Rendering.OrthographicZProjection.DrawTo(partGraphics2D, meshGroup.Mesh, meshGroup.WorldMatrix(), new Vector2(-bounds2D.Left + PartMarginMM, -bounds2D.Bottom + textSpaceMM + PartMarginMM), PixelsPerMM, Color.Black); + } + partGraphics2D.Render(typeFacePrinter, Color.Black); - RectangleDouble rectBounds = new RectangleDouble(0, 0, imageOfPart.Width, imageOfPart.Height); - double strokeWidth = .5 * PixelsPerMM; - rectBounds.Inflate(-strokeWidth / 2); - RoundedRect rect = new RoundedRect(rectBounds, PartMarginMM * PixelsPerMM); - partGraphics2D.Render(rect, Color.LightGray); - Stroke rectOutline = new Stroke(rect, strokeWidth); - partGraphics2D.Render(rectOutline, Color.DarkGray); + partImagesToPrint.Add(new PartImage(imageOfPart)); + } - foreach (var meshGroup in loadedMeshGroups) - { - PolygonMesh.Rendering.OrthographicZProjection.DrawTo(partGraphics2D, meshGroup.Mesh, meshGroup.WorldMatrix(), new Vector2(-bounds2D.Left + PartMarginMM, -bounds2D.Bottom + textSpaceMM + PartMarginMM), PixelsPerMM, Color.Black); - } - partGraphics2D.Render(typeFacePrinter, Color.Black); + reporter?.Invoke(Math.Min(processCount / itemSource.Count, .95), null); + processCount++; + } - partImagesToPrint.Add(new PartImage(imageOfPart)); - } + reporter?.Invoke(0, "Saving".Localize()); - progressStatus.Progress0To1 = Math.Min(processCount / itemSource.Count, .95); - reporter.Report(progressStatus); - processCount++; - } + partImagesToPrint.Sort(BiggestToLittlestImages); - progressStatus.Status = "Saving".Localize(); - reporter.Report(progressStatus); + PdfDocument document = new PdfDocument(); + document.Info.Title = "MatterHackers Parts Sheet"; + document.Info.Author = "MatterHackers Inc."; + document.Info.Subject = "This is a list of the parts that are in a queue from MatterControl."; + document.Info.Keywords = "MatterControl, STL, 3D Printing"; - partImagesToPrint.Sort(BiggestToLittlestImages); + int nextPartToPrintIndex = 0; + int plateNumber = 1; - PdfDocument document = new PdfDocument(); - document.Info.Title = "MatterHackers Parts Sheet"; - document.Info.Author = "MatterHackers Inc."; - document.Info.Subject = "This is a list of the parts that are in a queue from MatterControl."; - document.Info.Keywords = "MatterControl, STL, 3D Printing"; + while (nextPartToPrintIndex < partImagesToPrint.Count) + { + PdfPage pdfPage = document.AddPage(); + CreateOnePage(plateNumber++, ref nextPartToPrintIndex, pdfPage); + } - int nextPartToPrintIndex = 0; - int plateNumber = 1; - bool done = false; + try + { + // save the final document + document.Save(pathAndFileToSaveTo); - while (!done && nextPartToPrintIndex < partImagesToPrint.Count) - { - PdfPage pdfPage = document.AddPage(); - CreateOnePage(plateNumber++, ref nextPartToPrintIndex, pdfPage); - } + if (openAfterSave) + { + // Now try and open the document. This will launch whatever PDF viewer is on the system and ask it + // to show the file (at least on Windows). + ApplicationController.ProcessStart(pathAndFileToSaveTo); + } + } + catch (Exception) + { + } - try - { - // save the final document - document.Save(pathAndFileToSaveTo); + currentlySaving = false; - // Now try and open the document. This will launch whatever PDF viewer is on the system and ask it - // to show the file (at least on Windows). - ApplicationController.ProcessStart(pathAndFileToSaveTo); - } - catch (Exception) - { - } + reporter?.Invoke(1, null); + } - currentlySaving = false; + public async Task SaveSheets(Action reporter = null) + { + if (reporter == null) + { + await ApplicationController.Instance.Tasks.Execute("Export Part Sheet".Localize(), null, ExportTask); + } + else + { + await ExportTask(reporter, new CancellationTokenSource()); + } + } - progressStatus.Progress0To1 = 1; - reporter.Report(progressStatus); - }); - } + private static int BiggestToLittlestImages(PartImage one, PartImage two) + { + return two.image.Height.CompareTo(one.image.Height); + } - private static int BiggestToLittlestImages(PartImage one, PartImage two) - { - return two.image.Height.CompareTo(one.image.Height); - } + private void CreateOnePage(int plateNumber, ref int nextPartToPrintIndex, PdfPage pdfPage) + { + ImageBuffer plateInventoryImage = new ImageBuffer((int)(SheetSizePixels.X), (int)(SheetSizePixels.Y)); + Graphics2D plateGraphics = plateInventoryImage.NewGraphics2D(); + double currentlyPrintingHeightPixels = PrintTopOfPage(plateInventoryImage, plateGraphics); - private void CreateOnePage(int plateNumber, ref int nextPartToPrintIndex, PdfPage pdfPage) - { - ImageBuffer plateInventoryImage = new ImageBuffer((int)(SheetSizePixels.X), (int)(SheetSizePixels.Y)); - Graphics2D plateGraphics = plateInventoryImage.NewGraphics2D(); - double currentlyPrintingHeightPixels = PrintTopOfPage(plateInventoryImage, plateGraphics); + Vector2 offset = new Vector2(PageMarginPixels.Left, currentlyPrintingHeightPixels); + double tallestHeight = 0; + List partsOnLine = new List(); + while (nextPartToPrintIndex < partImagesToPrint.Count) + { + ImageBuffer image = partImagesToPrint[nextPartToPrintIndex].image; + tallestHeight = Math.Max(tallestHeight, image.Height); - Vector2 offset = new Vector2(PageMarginPixels.Left, currentlyPrintingHeightPixels); - double tallestHeight = 0; - List partsOnLine = new List(); - while (nextPartToPrintIndex < partImagesToPrint.Count) - { - ImageBuffer image = partImagesToPrint[nextPartToPrintIndex].image; - tallestHeight = Math.Max(tallestHeight, image.Height); + if (partsOnLine.Count > 0 && offset.X + image.Width > plateInventoryImage.Width - PageMarginPixels.Right) + { + if (partsOnLine.Count == 1) + { + plateGraphics.Render(partsOnLine[0].image, plateInventoryImage.Width / 2 - partsOnLine[0].image.Width / 2, offset.Y - tallestHeight); + } + else + { + foreach (PartImage partToDraw in partsOnLine) + { + plateGraphics.Render(partToDraw.image, partToDraw.xOffset, offset.Y - tallestHeight); + } + } - if (partsOnLine.Count > 0 && offset.X + image.Width > plateInventoryImage.Width - PageMarginPixels.Right) - { - if (partsOnLine.Count == 1) - { - plateGraphics.Render(partsOnLine[0].image, plateInventoryImage.Width / 2 - partsOnLine[0].image.Width / 2, offset.Y - tallestHeight); - } - else - { - foreach (PartImage partToDraw in partsOnLine) - { - plateGraphics.Render(partToDraw.image, partToDraw.xOffset, offset.Y - tallestHeight); - } - } + offset.X = PageMarginPixels.Left; + offset.Y -= (tallestHeight + PartPaddingPixels * 2); + tallestHeight = 0; + partsOnLine.Clear(); + if (offset.Y - image.Height < PageMarginPixels.Bottom) + { + break; + } + } + else + { + partImagesToPrint[nextPartToPrintIndex].xOffset = offset.X; + partsOnLine.Add(partImagesToPrint[nextPartToPrintIndex]); + //plateGraphics.Render(image, offset.x, offset.y - image.Height); + offset.X += image.Width + PartPaddingPixels * 2; + nextPartToPrintIndex++; + } + } - offset.X = PageMarginPixels.Left; - offset.Y -= (tallestHeight + PartPaddingPixels * 2); - tallestHeight = 0; - partsOnLine.Clear(); - if (offset.Y - image.Height < PageMarginPixels.Bottom) - { - break; - } - } - else - { - partImagesToPrint[nextPartToPrintIndex].xOffset = offset.X; - partsOnLine.Add(partImagesToPrint[nextPartToPrintIndex]); - //plateGraphics.Render(image, offset.x, offset.y - image.Height); - offset.X += image.Width + PartPaddingPixels * 2; - nextPartToPrintIndex++; - } - } + // print the last line of parts + foreach (PartImage partToDraw in partsOnLine) + { + plateGraphics.Render(partToDraw.image, partToDraw.xOffset, offset.Y - tallestHeight); + } - // print the last line of parts - foreach (PartImage partToDraw in partsOnLine) - { - plateGraphics.Render(partToDraw.image, partToDraw.xOffset, offset.Y - tallestHeight); - } + TypeFacePrinter printer = new TypeFacePrinter(string.Format("{0}", Path.GetFileNameWithoutExtension(pathAndFileToSaveTo)), 32, justification: Justification.Center); + printer.Origin = new Vector2(plateGraphics.DestImage.Width / 2, 110); + plateGraphics.Render(printer, Color.Black); - TypeFacePrinter printer = new TypeFacePrinter(string.Format("{0}", Path.GetFileNameWithoutExtension(pathAndFileToSaveTo)), 32, justification: Justification.Center); - printer.Origin = new Vector2(plateGraphics.DestImage.Width / 2, 110); - plateGraphics.Render(printer, Color.Black); + printer = new TypeFacePrinter(string.Format("Page {0}", plateNumber), 28, justification: Justification.Center); + printer.Origin = new Vector2(plateGraphics.DestImage.Width / 2, 60); + plateGraphics.Render(printer, Color.Black); - printer = new TypeFacePrinter(string.Format("Page {0}", plateNumber), 28, justification: Justification.Center); - printer.Origin = new Vector2(plateGraphics.DestImage.Width / 2, 60); - plateGraphics.Render(printer, Color.Black); + MemoryStream jpegStream = new MemoryStream(); + ImageIO.SaveImageData(jpegStream, ".jpeg", plateInventoryImage); - MemoryStream jpegStream = new MemoryStream(); - ImageIO.SaveImageData(jpegStream, ".jpeg", plateInventoryImage); + XGraphics gfx = XGraphics.FromPdfPage(pdfPage); + jpegStream.Seek(0, SeekOrigin.Begin); + XImage jpegImage = XImage.FromStream(jpegStream); + //double width = jpegImage.PixelWidth * 72 / jpegImage.HorizontalResolution; + //double height = jpegImage.PixelHeight * 72 / jpegImage. .HorizontalResolution; - XGraphics gfx = XGraphics.FromPdfPage(pdfPage); - jpegStream.Seek(0, SeekOrigin.Begin); - XImage jpegImage = XImage.FromStream(jpegStream); - //double width = jpegImage.PixelWidth * 72 / jpegImage.HorizontalResolution; - //double height = jpegImage.PixelHeight * 72 / jpegImage. .HorizontalResolution; + gfx.DrawImage(jpegImage, 0, 0, pdfPage.Width, pdfPage.Height); + } - gfx.DrawImage(jpegImage, 0, 0, pdfPage.Width, pdfPage.Height); - } + private double PrintTopOfPage(ImageBuffer plateInventoryImage, Graphics2D plateGraphics) + { + plateGraphics.Clear(Color.White); - private double PrintTopOfPage(ImageBuffer plateInventoryImage, Graphics2D plateGraphics) - { - plateGraphics.Clear(Color.White); + double currentlyPrintingHeightPixels = plateInventoryImage.Height - PageMarginPixels.Top; - double currentlyPrintingHeightPixels = plateInventoryImage.Height - PageMarginPixels.Top; + string logoPathAndFile = Path.Combine("Images", "PartSheetLogo.png"); + if (StaticData.Instance.FileExists(logoPathAndFile)) + { + ImageBuffer logoImage = StaticData.Instance.LoadImage(logoPathAndFile); + currentlyPrintingHeightPixels -= logoImage.Height; + plateGraphics.Render(logoImage, (plateInventoryImage.Width - logoImage.Width) / 2, currentlyPrintingHeightPixels); + } - string logoPathAndFile = Path.Combine("Images", "PartSheetLogo.png"); - if (StaticData.Instance.FileExists(logoPathAndFile)) - { - ImageBuffer logoImage = StaticData.Instance.LoadImage(logoPathAndFile); - currentlyPrintingHeightPixels -= logoImage.Height; - plateGraphics.Render(logoImage, (plateInventoryImage.Width - logoImage.Width) / 2, currentlyPrintingHeightPixels); - } + currentlyPrintingHeightPixels -= PartPaddingPixels; - currentlyPrintingHeightPixels -= PartPaddingPixels; + double underlineHeightMM = 1; - double underlineHeightMM = 1; + var lineBounds = new RectangleDouble(0, 0, plateInventoryImage.Width - PageMarginPixels.Left * 2, underlineHeightMM * PixelsPerMM); + lineBounds.Offset(PageMarginPixels.Left, currentlyPrintingHeightPixels - lineBounds.Height); + plateGraphics.FillRectangle(lineBounds, Color.Black); - var lineBounds = new RectangleDouble(0, 0, plateInventoryImage.Width - PageMarginPixels.Left * 2, underlineHeightMM * PixelsPerMM); - lineBounds.Offset(PageMarginPixels.Left, currentlyPrintingHeightPixels - lineBounds.Height); - plateGraphics.FillRectangle(lineBounds, Color.Black); + return currentlyPrintingHeightPixels - (lineBounds.Height + PartPaddingPixels); + } - return currentlyPrintingHeightPixels - (lineBounds.Height + PartPaddingPixels); - } + public class FileNameAndPresentationName + { + public string fileName; + public string presentationName; - public static bool IsSaving() - { - return currentlySaving; - } - } + public FileNameAndPresentationName(string fileName, string presentationName) + { + this.fileName = fileName; + this.presentationName = presentationName; + } + } + + internal class PartImage + { + internal ImageBuffer image; + internal bool wasDrawn = false; + internal double xOffset = 0; + + public PartImage(ImageBuffer imageOfPart) + { + this.image = imageOfPart; + } + } + } } \ No newline at end of file diff --git a/PrinterDriverInstaller/InfInstaller.csproj b/PrinterDriverInstaller/InfInstaller.csproj index 1bc0a2ca9..cb18216d9 100644 --- a/PrinterDriverInstaller/InfInstaller.csproj +++ b/PrinterDriverInstaller/InfInstaller.csproj @@ -8,7 +8,7 @@ - + all diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index 03b9b70da..6f4f7acb7 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -2083,6 +2083,9 @@ Translated:Generate Supports English:Generates an outline around the support material to improve strength and hold up interface layers. Translated:Generates an outline around the support material to improve strength and hold up interface layers. +English:Generating Config +Translated:Generating Config + English:Generating Lithophane Translated:Generating Lithophane @@ -4933,6 +4936,9 @@ Translated:Starting Angle English:Starting firmware update... Translated:Starting firmware update... +English:Starting slicer +Translated:Starting slicer + English:Starting Sync Translated:Starting Sync diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index 3e48ee30a..7d1dcff6c 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit 3e48ee30a5a4ab9ad10c74be5526a513dad92983 +Subproject commit 7d1dcff6c5569573c0cb3312a965dd568595cc88 diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 8d9bb9ee8..3b25709cb 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 8d9bb9ee85248a201d83f08ab7eb6d4b0f696ef4 +Subproject commit 3b25709cbf149f8e6189ee8eeae7687726391ab4 diff --git a/Tests/MatterControl.AutomationTests/MatterControl.AutomationTests.csproj b/Tests/MatterControl.AutomationTests/MatterControl.AutomationTests.csproj index e977c8735..b256c24ad 100644 --- a/Tests/MatterControl.AutomationTests/MatterControl.AutomationTests.csproj +++ b/Tests/MatterControl.AutomationTests/MatterControl.AutomationTests.csproj @@ -14,11 +14,11 @@ $(AssemblyName) - - + + - - + + all diff --git a/Tests/MatterControl.Tests/MatterControl.Tests.csproj b/Tests/MatterControl.Tests/MatterControl.Tests.csproj index 46aef034e..bf4cacc28 100644 --- a/Tests/MatterControl.Tests/MatterControl.Tests.csproj +++ b/Tests/MatterControl.Tests/MatterControl.Tests.csproj @@ -39,11 +39,11 @@ - - + + - - + + all