diff --git a/UVtools.ScriptSample/ScriptDebandingZSample.cs b/UVtools.ScriptSample/ScriptDebandingZSample.cs
new file mode 100644
index 0000000..5d33739
--- /dev/null
+++ b/UVtools.ScriptSample/ScriptDebandingZSample.cs
@@ -0,0 +1,166 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using UVtools.Core.Extensions;
+using UVtools.Core.Scripting;
+
+namespace UVtools.ScriptSample
+{
+ ///
+ /// Change layer properties to random values
+ ///
+ public class ScriptDebandingZSample : ScriptGlobals
+ {
+ ScriptCheckBoxInput CreateEmptyLayerInput = new()
+ {
+ Label = "Create a first empty layer to overcome printer firmware limitation",
+ ToolTip = "Some printers will not respect wait time for the first layer, introducing the problem once again. Use this option to by pass that",
+ Value = true
+ };
+
+ ScriptNumericalInput BottomSafeDebandingHeightInput = new()
+ {
+ Label = "Safe height for the large debanding time",
+ //ToolTip = "Margin in pixels to inset from object edge",
+ Unit = "mm",
+ Minimum = 0.1m,
+ Maximum = 50,
+ Increment = 0.5m,
+ Value = 0.5m,
+ DecimalPlates = 2,
+ };
+
+ ScriptNumericalInput BottomWaitTimeBeforeCureInput = new()
+ {
+ Label = "Large debanding wait time before cure",
+ ToolTip = "Time to wait before cure a debanding layer",
+ Unit = "s",
+ Minimum = 5,
+ Maximum = 300,
+ Increment = 1,
+ Value = 40,
+ DecimalPlates = 2,
+ };
+
+ ScriptNumericalInput NormalWaitTimeBeforeCureInput = new()
+ {
+ Label = "Normal wait time before cure",
+ ToolTip = "Time to wait before cure a normal layer",
+ Unit = "s",
+ Minimum = 1,
+ Maximum = 300,
+ Increment = 1,
+ Value = 3,
+ DecimalPlates = 2,
+ };
+
+ ScriptNumericalInput BottomWaitTimeAfterCureInput = new()
+ {
+ Label = "Bottom wait time after cure",
+ ToolTip = "Time to wait after cure a bottom layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 100,
+ Increment = 1,
+ Value = 5,
+ DecimalPlates = 2,
+ };
+
+ ScriptNumericalInput NormalWaitTimeAfterCureInput = new()
+ {
+ Label = "Normal wait time after cure",
+ ToolTip = "Time to wait after cure a normal layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 100,
+ Increment = 1,
+ Value = 2,
+ DecimalPlates = 2,
+ };
+
+ ///
+ /// Set configurations here, this function trigger just after load a script
+ ///
+ public void ScriptInit()
+ {
+ Script.Name = "Debanding Z with wait time";
+ Script.Description = "Applies wait time at certain layers to help layer adhesion and deband the Z axis.\n" +
+ "Based on the guide: https://bit.ly/3nkXAOa\n";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ if (SlicerFile.SupportsGCode) CreateEmptyLayerInput.Value = false;
+ Script.UserInputs.Add(CreateEmptyLayerInput);
+ Script.UserInputs.Add(BottomSafeDebandingHeightInput);
+ Script.UserInputs.Add(BottomWaitTimeBeforeCureInput);
+ Script.UserInputs.Add(NormalWaitTimeBeforeCureInput);
+ if(SlicerFile.CanUseBottomWaitTimeAfterCure) Script.UserInputs.Add(BottomWaitTimeAfterCureInput);
+ if(SlicerFile.CanUseWaitTimeAfterCure) Script.UserInputs.Add(NormalWaitTimeAfterCureInput);
+ }
+
+ ///
+ /// Validate user inputs here, this function trigger when user click on execute
+ ///
+ /// A error message, empty or null if validation passes.
+ public string ScriptValidate()
+ {
+ return SlicerFile.CanUseAnyLightOffDelay || SlicerFile.CanUseAnyWaitTimeBeforeCure ? null : "Your printer/file format is not supported.";
+ }
+
+ ///
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ ///
+ /// True if executes successfully to the end, otherwise false.
+ public bool ScriptExecute()
+ {
+ Progress.Reset("Changing layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
+
+ if (SlicerFile.CanUseAnyWaitTime)
+ {
+ SlicerFile.BottomLightOffDelay = 0;
+ SlicerFile.LightOffDelay = 0;
+ SlicerFile.BottomWaitTimeBeforeCure = (float) BottomWaitTimeBeforeCureInput.Value;
+
+ SlicerFile.WaitTimeBeforeCure = (float)NormalWaitTimeBeforeCureInput.Value;
+ }
+ else
+ {
+ SlicerFile.SetBottomLightOffDelay((float)BottomWaitTimeBeforeCureInput.Value);
+ SlicerFile.SetNormalLightOffDelay((float)NormalWaitTimeBeforeCureInput.Value);
+ }
+
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure) SlicerFile.BottomWaitTimeAfterCure = (float) BottomWaitTimeAfterCureInput.Value;
+ if (SlicerFile.CanUseWaitTimeAfterCure) SlicerFile.WaitTimeAfterCure = (float)NormalWaitTimeAfterCureInput.Value;
+
+ foreach (var layer in SlicerFile)
+ {
+ if((decimal)layer.PositionZ > BottomSafeDebandingHeightInput.Value) break;
+ layer.SetWaitTimeBeforeCureOrLightOffDelay((float) BottomWaitTimeBeforeCureInput.Value);
+ }
+
+ if (CreateEmptyLayerInput.Value)
+ {
+ var firstLayer = SlicerFile.FirstLayer.Clone();
+ using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ var pixelPos = firstLayer.BoundingRectangle.Center();
+ mat.SetByte(pixelPos.X, pixelPos.Y, 1); // Print a very fade pixel to ignore empty layer detection
+ firstLayer.LayerMat = mat;
+ firstLayer.ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.1f;
+ firstLayer.SetNoDelays();
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerManager.Prepend(firstLayer);
+ });
+
+ }
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
+ }
+ }
+}