mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-09 18:12:32 +02:00
554794d0d8
- **Core:**
- (Add) Utilities methods (`MinimumSpeed`, `MaximumSpeed`, `CreateMat`, `CreateMatWithDummyPixel`, `FileFormat.CopyParameters`)
- (Improvement) Issues - Print Height: Group all layers outside the valid print height
- **FileFormats:**
- (Add) Generic / Phrozen ZIP format
- (Add) Information/modifier to file formats to tell whatever is possible to use custom PositionZ per layer
- (Add) Safe checks in order to run some tools, related to the previous "PositionZ" point
- (Improvement) if blank, allow the previous layer to have a higher Z position than the successor layer
- (Improvement) SL1: Implement the missing keys from new features of PrusaSlicer 2.4.0
- (Fix) Calling a partial save action without a progress instance would cause a crash
- (Fix) GCode: Unable to parse the "Wait time after lift" when a second lift (TSMC) was present, leading to a sum on "Wait time before cure"
- **Tools:**
- (Add) Timelapse: Raise the build platform to a set position every odd-even height to be able to take a photo and create a time-lapse video of the print
- (Add) Scripting: ScriptToggleSwitchInput
- **Raise on print finish:**
- (Add) Reapply check and prevent run the tool in that case
- (Fix) It was incorrectly marked to be able to run in partial mode
- (Fix) The dummy pixel was beeing set to a wrong location
- **Layers:**:
- (Change) When set Wait times to a negative value, it will set the global wait time value accordingly
- (Change) Allow ExposureTime to be 0
- **Commandline arguments**
- (Add) --run-operation \<input_file\> \<operation_file.uvtop\>
- (Add) --run-script \<input_file\> \<script_file.cs\>
- (Add) --copy-parameters \<from_file\> \<to_file\>
- **UI:**
- (Add) When open a file with missing crucial information, prompt the user to fill in that information (Optional)
- (Add) Warn user about misfunction when open a file with invalid layer height of 0mm
- (Improvement) Layer information: Only show the "Exposure time" when its availiable on the file format
- (Improvement) When a file is about to get auto-converted once loaded and the output file already exists, prompt user for overwrite action
- (Improvement) Set default culture info at top most of the program to avoid strange problems with commandline arguments
- (Upgrade) .NET from 5.0.13 to 5.0.14
- (Downgrade) OpenCV from 4.5.5 to 4.5.4 due the crash while detecting islands (Linux and MacOS) (#411, #415, #416)
425 lines
18 KiB
C#
425 lines
18 KiB
C#
/*
|
|
* GNU AFFERO GENERAL PUBLIC LICENSE
|
|
* Version 3, 19 November 2007
|
|
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
* Everyone is permitted to copy and distribute verbatim copies
|
|
* of this license document, but changing it is not allowed.
|
|
*/
|
|
|
|
using System;
|
|
using UVtools.Core.Scripting;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using UVtools.Core.FileFormats;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
namespace UVtools.ScriptSample
|
|
{
|
|
/// <summary>
|
|
/// Performs a black inset around objects
|
|
/// </summary>
|
|
public class ScriptCloneSettings : ScriptGlobals
|
|
{
|
|
ScriptCheckBoxInput Recursive = new()
|
|
{
|
|
Label = "Recursive",
|
|
ToolTip = "If unchecked, only files in the same folder are modified; if checked, files in all sub-folders are modified too",
|
|
Value = true
|
|
};
|
|
|
|
ScriptCheckBoxInput Report = new()
|
|
{
|
|
Label = "Generate report file",
|
|
ToolTip = "Optionally generate a report file in the same directory as the open file",
|
|
Value = true
|
|
};
|
|
|
|
ScriptCheckBoxInput OpenReport = new()
|
|
{
|
|
Label = "Open report file",
|
|
ToolTip = "Optionally open the result file when completed",
|
|
Value = true
|
|
};
|
|
|
|
ScriptOpenFolderDialogInput FolderPath = new()
|
|
{
|
|
Label = "Folder path",
|
|
ToolTip = "The folder path to process",
|
|
};
|
|
|
|
private enum ResultStatus
|
|
{
|
|
Exception,
|
|
UnknownFileType,
|
|
DifferentMachineTypes,
|
|
Success,
|
|
Unchanged,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set configurations here, this function trigger just after load a script
|
|
/// </summary>
|
|
public void ScriptInit()
|
|
{
|
|
Script.Name = "Clone Settings";
|
|
Script.Description = "Copies the print settings from the current file to all files in the same directory and, optionally, recursively below it";
|
|
Script.Author = "Gina Venolia";
|
|
Script.Version = new Version(0, 2);
|
|
Script.UserInputs.Add(Recursive);
|
|
Script.UserInputs.Add(Report);
|
|
Script.UserInputs.Add(OpenReport);
|
|
Script.UserInputs.Add(FolderPath);
|
|
FolderPath.Value = SlicerFile.DirectoryPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate user inputs here, this function trigger when user click on execute
|
|
/// </summary>
|
|
/// <returns>A error message, empty or null if validation passes.</returns>
|
|
public string ScriptValidate()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute the script, this function trigger when when user click on execute and validation passes
|
|
/// </summary>
|
|
/// <returns>True if executes successfully to the end, otherwise false.</returns>
|
|
public bool ScriptExecute()
|
|
{
|
|
Progress.CanCancel = true;
|
|
|
|
// Gather the list of directories to operate on
|
|
Progress.Reset("Processing files...");
|
|
|
|
var directoryPaths = new List<string>();
|
|
directoryPaths.Add(Path.GetDirectoryName(SlicerFile.FileFullPath));
|
|
|
|
if (Recursive.Value)
|
|
{
|
|
for (var i = 0; i < directoryPaths.Count; i++)
|
|
{
|
|
directoryPaths.AddRange(Directory.EnumerateDirectories(directoryPaths[i]));
|
|
}
|
|
}
|
|
|
|
// Gather the list of files to operate on
|
|
var filePaths = new List<string>();
|
|
foreach (var directoryPath in directoryPaths)
|
|
{
|
|
filePaths.AddRange(Directory.EnumerateFiles(directoryPath));
|
|
}
|
|
|
|
// Except for the file we started with
|
|
var normalizedFilePath = Path.GetFullPath(SlicerFile.FileFullPath);
|
|
filePaths.RemoveAll(x => Path.GetFullPath(x) == normalizedFilePath);
|
|
|
|
// Process the files
|
|
Progress.Reset("Processing files...", (uint)filePaths.Count, 0);
|
|
|
|
var results = new List<Tuple<ResultStatus, string, List<string>>>();
|
|
foreach (var filePath in filePaths)
|
|
{
|
|
if (Progress.Token.IsCancellationRequested) return false;
|
|
|
|
Tuple<ResultStatus, string, List<string>> result;
|
|
try
|
|
{
|
|
result = ProcessFile(filePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var deets = new List<string>();
|
|
deets.Add(ex.GetType().Name);
|
|
deets.Add(ex.Message);
|
|
result = new Tuple<ResultStatus, string, List<string>>(ResultStatus.Exception, filePath, deets);
|
|
}
|
|
|
|
results.Add(result);
|
|
|
|
Progress++;
|
|
}
|
|
|
|
// Generate the report
|
|
if (Report.Value)
|
|
{
|
|
var reportDirectory = Path.GetDirectoryName(SlicerFile.FileFullPath);
|
|
var reportFilePath = Path.Combine(reportDirectory, "CloneSettingsReport.txt");
|
|
using (var writer = new StreamWriter(reportFilePath))
|
|
{
|
|
writer.WriteLine(Script.Name + " " + Script.Version.ToString() + " by " + Script.Author);
|
|
writer.WriteLine("Source file: " + SlicerFile.FileFullPath);
|
|
writer.WriteLine(DateTime.Now.ToLongDateString() + ", " + DateTime.Now.ToLongTimeString());
|
|
writer.WriteLine("Recursive: " + Recursive.Value.ToString());
|
|
writer.WriteLine("Directories: " + directoryPaths.Count);
|
|
writer.WriteLine("Files: " + filePaths.Count);
|
|
writer.WriteLine();
|
|
|
|
foreach (var statusGroup in results.GroupBy(x => x.Item1))
|
|
{
|
|
writer.WriteLine(statusGroup.Key.ToString());
|
|
foreach (var result in statusGroup.OrderBy(x => x.Item2))
|
|
{
|
|
writer.WriteLine("\t" + result.Item2);
|
|
if (result.Item3 is not null)
|
|
{
|
|
foreach (var detail in result.Item3)
|
|
{
|
|
writer.WriteLine("\t\t" + detail);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OpenReport.Value)
|
|
{
|
|
var startInfo = new ProcessStartInfo(reportFilePath)
|
|
{
|
|
UseShellExecute = true
|
|
};
|
|
Process.Start(startInfo);
|
|
}
|
|
}
|
|
|
|
// return true if not cancelled by user
|
|
return true;
|
|
}
|
|
|
|
private Tuple<ResultStatus, string, List<string>> ProcessFile(string filePath)
|
|
{
|
|
// Determine the type of file
|
|
var file = FileFormat.FindByExtensionOrFilePath(filePath, true);
|
|
if (file is null)
|
|
{
|
|
return new Tuple<ResultStatus, string, List<string>>(ResultStatus.UnknownFileType, filePath, null);
|
|
}
|
|
|
|
// Load the file
|
|
file.Decode(filePath);
|
|
if (string.Compare(file.MachineName, SlicerFile.MachineName, true) != 0)
|
|
{
|
|
var deets = new List<string>();
|
|
deets.Add("Source machine type: " + SlicerFile.MachineName);
|
|
deets.Add("Destination machine type: " + file.MachineName);
|
|
return new Tuple<ResultStatus, string, List<string>>(ResultStatus.DifferentMachineTypes, filePath, deets);
|
|
}
|
|
|
|
// TODO: Validate that some parameters are the same in both files?
|
|
|
|
// Change the parameters
|
|
var changed = false;
|
|
var details = new List<string>();
|
|
|
|
if (file.CanUseBottomLayerCount && file.BottomLayerCount != SlicerFile.BottomLayerCount)
|
|
{
|
|
details.Add($"Bottom Layer Count: {file.BottomLayerCount} to {SlicerFile.BottomLayerCount}");
|
|
file.BottomLayerCount = SlicerFile.BottomLayerCount;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLightOffDelay && file.BottomLightOffDelay != SlicerFile.BottomLightOffDelay)
|
|
{
|
|
details.Add($"Bottom Light Off Delay: {file.BottomLightOffDelay} to {SlicerFile.BottomLightOffDelay}");
|
|
file.BottomLightOffDelay = SlicerFile.BottomLightOffDelay;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLightOffDelay && file.LightOffDelay != SlicerFile.LightOffDelay)
|
|
{
|
|
details.Add($"Light Off Delay: {file.LightOffDelay} to {SlicerFile.LightOffDelay}");
|
|
file.LightOffDelay = SlicerFile.LightOffDelay;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeBeforeCure && file.BottomWaitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure)
|
|
{
|
|
details.Add($"Bottom Wait Time Before Cure: {file.BottomWaitTimeBeforeCure} to {SlicerFile.BottomWaitTimeBeforeCure}");
|
|
file.BottomWaitTimeBeforeCure = SlicerFile.BottomWaitTimeBeforeCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeBeforeCure && file.WaitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure)
|
|
{
|
|
details.Add($"Wait Time Before Cure: {file.WaitTimeBeforeCure} to {SlicerFile.WaitTimeBeforeCure}");
|
|
file.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomExposureTime && file.BottomExposureTime != SlicerFile.BottomExposureTime)
|
|
{
|
|
details.Add($"Bottom Exposure Time: {file.BottomExposureTime} to {SlicerFile.BottomExposureTime}");
|
|
file.BottomExposureTime = SlicerFile.BottomExposureTime;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseExposureTime && file.ExposureTime != SlicerFile.ExposureTime)
|
|
{
|
|
details.Add($"Exposure Time: {file.ExposureTime} to {SlicerFile.ExposureTime}");
|
|
file.ExposureTime = SlicerFile.ExposureTime;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeAfterCure && file.BottomWaitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure)
|
|
{
|
|
details.Add($"Bottom Wait Time After Cure: {file.BottomWaitTimeAfterCure} to {SlicerFile.BottomWaitTimeAfterCure}");
|
|
file.BottomWaitTimeAfterCure = SlicerFile.BottomWaitTimeAfterCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeAfterCure && file.WaitTimeAfterCure != SlicerFile.WaitTimeAfterCure)
|
|
{
|
|
details.Add($"Wait Time After Cure: {file.WaitTimeAfterCure} to {SlicerFile.WaitTimeAfterCure}");
|
|
file.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftHeight && file.BottomLiftHeight != SlicerFile.BottomLiftHeight)
|
|
{
|
|
details.Add($"Bottom Lift Height: {file.BottomLiftHeight} to {SlicerFile.BottomLiftHeight}");
|
|
file.BottomLiftHeight = SlicerFile.BottomLiftHeight;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftSpeed && file.BottomLiftSpeed != SlicerFile.BottomLiftSpeed)
|
|
{
|
|
details.Add($"Bottom Lift Speed: {file.BottomLiftSpeed} to {SlicerFile.BottomLiftSpeed}");
|
|
file.BottomLiftSpeed = SlicerFile.BottomLiftSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftHeight && file.LiftHeight != SlicerFile.LiftHeight)
|
|
{
|
|
details.Add($"Lift Height: {file.LiftHeight} to {SlicerFile.LiftHeight}");
|
|
file.LiftHeight = SlicerFile.LiftHeight;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftSpeed && file.LiftSpeed != SlicerFile.LiftSpeed)
|
|
{
|
|
details.Add($"Lift Speed: {file.LiftSpeed} to {SlicerFile.LiftSpeed}");
|
|
file.LiftSpeed = SlicerFile.LiftSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftHeight2 && file.BottomLiftHeight2 != SlicerFile.BottomLiftHeight2)
|
|
{
|
|
details.Add($"Bottom Lift Height 2: {file.BottomLiftHeight2} to {SlicerFile.BottomLiftHeight2}");
|
|
file.BottomLiftHeight2 = SlicerFile.BottomLiftHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLiftSpeed2 && file.BottomLiftSpeed2 != SlicerFile.BottomLiftSpeed2)
|
|
{
|
|
details.Add($"Bottom Lift Speed2: {file.BottomLiftSpeed2} to {SlicerFile.BottomLiftSpeed2}");
|
|
file.BottomLiftSpeed2 = SlicerFile.BottomLiftSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftHeight2 && file.LiftHeight2 != SlicerFile.LiftHeight2)
|
|
{
|
|
details.Add($"Lift Height 2: {file.LiftHeight2} to {SlicerFile.LiftHeight2}");
|
|
file.LiftHeight2 = SlicerFile.LiftHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLiftSpeed2 && file.LiftSpeed2 != SlicerFile.LiftSpeed2)
|
|
{
|
|
details.Add($"Lift Speed 2: {file.LiftSpeed2} to {SlicerFile.LiftSpeed2}");
|
|
file.LiftSpeed2 = SlicerFile.LiftSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomWaitTimeAfterLift && file.BottomWaitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift)
|
|
{
|
|
details.Add($"Bottom Wait Time After Lift: {file.BottomWaitTimeAfterLift} to {SlicerFile.BottomWaitTimeAfterLift}");
|
|
file.BottomWaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseWaitTimeAfterLift && file.WaitTimeAfterLift != SlicerFile.WaitTimeAfterLift)
|
|
{
|
|
details.Add($"Wait Time After Lift: {file.WaitTimeAfterLift} to {SlicerFile.WaitTimeAfterLift}");
|
|
file.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractSpeed && file.BottomRetractSpeed != SlicerFile.BottomRetractSpeed)
|
|
{
|
|
details.Add($"Bottom Retract Speed: {file.BottomRetractSpeed} to {SlicerFile.BottomRetractSpeed}");
|
|
file.BottomRetractSpeed = SlicerFile.BottomRetractSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractSpeed && file.RetractSpeed != SlicerFile.RetractSpeed)
|
|
{
|
|
details.Add($"Retract Speed: {file.RetractSpeed} to {SlicerFile.RetractSpeed}");
|
|
file.RetractSpeed = SlicerFile.RetractSpeed;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractHeight2 && file.BottomRetractHeight2 != SlicerFile.BottomRetractHeight2)
|
|
{
|
|
details.Add($"Bottom Retract Height 2: {file.BottomRetractHeight2} to {SlicerFile.BottomRetractHeight2}");
|
|
file.BottomRetractHeight2 = SlicerFile.BottomRetractHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomRetractSpeed2 && file.BottomRetractSpeed2 != SlicerFile.BottomRetractSpeed2)
|
|
{
|
|
details.Add($"Bottom Retract Speed2: {file.BottomRetractSpeed2} to {SlicerFile.BottomRetractSpeed2}");
|
|
file.BottomRetractSpeed2 = SlicerFile.BottomRetractSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractHeight2 && file.RetractHeight2 != SlicerFile.RetractHeight2)
|
|
{
|
|
details.Add($"Retract Height 2: {file.RetractHeight2} to {SlicerFile.RetractHeight2}");
|
|
file.RetractHeight2 = SlicerFile.RetractHeight2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseRetractSpeed2 && file.RetractSpeed2 != SlicerFile.RetractSpeed2)
|
|
{
|
|
details.Add($"Retract Speed 2: {file.RetractSpeed2} to {SlicerFile.RetractSpeed2}");
|
|
file.RetractSpeed2 = SlicerFile.RetractSpeed2;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseBottomLightPWM && file.BottomLightPWM != SlicerFile.BottomLightPWM)
|
|
{
|
|
details.Add($"Bottom Light PWM: {file.BottomLightPWM} to {SlicerFile.BottomLightPWM}");
|
|
file.BottomLightPWM = SlicerFile.BottomLightPWM;
|
|
changed = true;
|
|
}
|
|
|
|
if (file.CanUseLightPWM && file.LightPWM != SlicerFile.LightPWM)
|
|
{
|
|
details.Add($"Light PWM: {file.LightPWM} to {SlicerFile.LightPWM}");
|
|
file.LightPWM = SlicerFile.LightPWM;
|
|
changed = true;
|
|
}
|
|
|
|
// if (file.CanUseXXX && file.XXX != SlicerFile.XXX)
|
|
// {
|
|
// details.Add($"XXX: {file.XXX} to {SlicerFile.XXX}");
|
|
// file.XXX = SlicerFile.XXX;
|
|
// changed = true;
|
|
// }
|
|
|
|
// Bail out if not changed
|
|
if (!changed)
|
|
{
|
|
return new Tuple<ResultStatus, string, List<string>>(ResultStatus.Unchanged, filePath, null);
|
|
}
|
|
|
|
// Save the changed file
|
|
file.Save();
|
|
|
|
return new Tuple<ResultStatus, string, List<string>>(ResultStatus.Success, filePath, details);
|
|
}
|
|
|
|
}
|
|
}
|