* (Add) photons file format (Read-only)
* (Add) Allow mouse scroll wheel on layer slider and issue tracker to change layers (#81)
* (Add) Menu - Help - Open settings folder: To open user settings folder
* (Add) When a file doesn't have a print time field or it's 0, UVtools calculate the approximate time based on parameters
* (Add) Per layer settings override on UVtools layer core
* (Add) Tool - Edit print parameters: Allow change per layer settings on a layer range
* (Add) Tool Window - Layer range synchronization and lock for single layer navigation (Checkbox)
* (Add) Tool Window - Change the start layer index on range will also change the layer image on background
* (Improvement) Adapt every file format to accept per layer settings where possible
* (Improvement) Better gcode checks and per layer settings parses
* (Change) When converting to CTB, version 3 of the file will be used instead of version 2
* (Change) When converting to photon or cbddlp, version 2 of the file will be used instead of version 2
* (Change) New logo, thanks to (Vinicius Silva @photonsters)
* (Fix) MSI installer was creating multiple entries/uninstallers on windows Apps and Features (#79)
* (Fix) Release builder script (CreateRelease.WPF.ps1): Replace backslash with shash for zip releases (#82)
* (Fix) CWS file reader when come from Chitubox (#84)
* (Fix) CWS was introducing a big delay after each layer, LiftHeight was being used 2 times instead of LiftSpeed (#85)
* (Fix) CWS fix Build Direction property name, was lacking a whitespace
* (Fix) Layer bounds was being show for empty layers on 0x0 position with 1px wide
* (Fix) Empty layers caused miscalculation of print volume bounds
* (Fix) Recalculate GCode didn't unlock save button
* (Fix) Tool - Calculator - Light-Off Delay: Wasn't calculating bottom layers
* (Change) Drop a digit from program version for simplicity, now: MAJOR.MINOR.PATCH
  * **Major:** new UI, lots of new features, conceptual change, incompatible API changes, etc.
  * **Minor:** add functionality in a backwards-compatible manner
  * **Patch:** backwards-compatible bug fixes
* (Upgrade) Avalonia framework to preview6
This commit is contained in:
Tiago Conceição
2020-11-01 02:45:53 +00:00
parent 0a36e61e8c
commit 679f088811
55 changed files with 5904 additions and 302 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# Inventor
CAD/OldVersions/
UVtools.CAD/OldVersions/
.lockfile
# User-specific files
+30
View File
@@ -1,5 +1,35 @@
# Changelog
## 01/11/2020 - v1.1.0
* (Add) photons file format (Read-only)
* (Add) Allow mouse scroll wheel on layer slider and issue tracker to change layers (#81)
* (Add) Menu - Help - Open settings folder: To open user settings folder
* (Add) When a file doesn't have a print time field or it's 0, UVtools calculate the approximate time based on parameters
* (Add) Per layer settings override on UVtools layer core
* (Add) Tool - Edit print parameters: Allow change per layer settings on a layer range
* (Add) Tool Window - Layer range synchronization and lock for single layer navigation (Checkbox)
* (Add) Tool Window - Change the start layer index on range will also change the layer image on background
* (Improvement) Adapt every file format to accept per layer settings where possible
* (Improvement) Better gcode checks and per layer settings parses
* (Change) When converting to CTB, version 3 of the file will be used instead of version 2
* (Change) When converting to photon or cbddlp, version 2 of the file will be used instead of version 2
* (Change) New logo, thanks to (Vinicius Silva @photonsters)
* (Fix) MSI installer was creating multiple entries/uninstallers on windows Apps and Features (#79)
* (Fix) Release builder script (CreateRelease.WPF.ps1): Replace backslash with shash for zip releases (#82)
* (Fix) CWS file reader when come from Chitubox (#84)
* (Fix) CWS was introducing a big delay after each layer, LiftHeight was being used 2 times instead of LiftSpeed (#85)
* (Fix) CWS fix Build Direction property name, was lacking a whitespace
* (Fix) Layer bounds was being show for empty layers on 0x0 position with 1px wide
* (Fix) Empty layers caused miscalculation of print volume bounds
* (Fix) Recalculate GCode didn't unlock save button
* (Fix) Tool - Calculator - Light-Off Delay: Wasn't calculating bottom layers
* (Change) Drop a digit from program version for simplicity, now: MAJOR.MINOR.PATCH
* **Major:** new UI, lots of new features, conceptual change, incompatible API changes, etc.
* **Minor:** add functionality in a backwards-compatible manner
* **Patch:** backwards-compatible bug fixes
* (Upgrade) Avalonia framework to preview6
## 23/10/2020 - v1.0.0.2
* (Fix) ROI selection button on bottom was always disabled even when a region is selected
+29 -5
View File
@@ -1,9 +1,33 @@
# When using System.IO.Compression.ZipFile.CreateFromDirectory in PowerShell, it still uses backslashes in the zip paths
# despite this https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/mitigation-ziparchiveentry-fullname-path-separator
# Based upon post by Seth Jackson https://sethjackson.github.io/2016/12/17/path-separators/
#
# PowerShell 5 (WMF5) & 6
# Using class Keyword https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_Classes
#
# https://gist.github.com/lantrix/738ebfa616d5222a8b1db947793bc3fc
#
Add-Type -AssemblyName System.Text.Encoding
Add-Type -AssemblyName System.IO.Compression.FileSystem
class FixedEncoder : System.Text.UTF8Encoding {
FixedEncoder() : base($true) { }
[byte[]] GetBytes([string] $s)
{
$s = $s.Replace("\", "/");
return ([System.Text.UTF8Encoding]$this).GetBytes($s);
}
}
cd $PSScriptRoot
$version = (Get-Command UVtools.WPF\bin\Release\netcoreapp3.1\UVtools.dll).FileVersionInfo.FileVersion
$version = (Get-Command UVtools.WPF\bin\Release\netcoreapp3.1\UVtools.dll).FileVersionInfo.ProductVersion
echo "UVtools v$version"
Remove-Item "$PSScriptRoot\UVtools.WPF\bin\Release\netcoreapp3.1\Assets\usersettings.xml" -Recurse -ErrorAction Ignore
Add-Type -A System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.WPF\bin\Release\netcoreapp3.1", "$PSScriptRoot\UVtools.WPF\bin\UVtools_v$version.zip")
#[IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.WPF\bin\Release\netcoreapp3.1", "$PSScriptRoot\UVtools.WPF\bin\UVtools_v$version.zip")
[System.IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.WPF\bin\Release\netcoreapp3.1", "$PSScriptRoot\UVtools.WPF\bin\UVtools_v$version.zip", [System.IO.Compression.CompressionLevel]::Optimal, $false, [FixedEncoder]::new())
Copy-Item "$PSScriptRoot\UVtools.Installer\bin\Release\UVtools.msi" -Destination "$PSScriptRoot\UVtools.WPF\bin\UVtools_v$version.msi"
+2
View File
@@ -54,11 +54,13 @@ But also, i need victims for test subject. Proceed at your own risk!
* SL1 (PrusaSlicer)
* Zip (Chitubox)
* Photon (Chitubox)
* Photons (Chitubox) [Read-only]
* CBDDLP (Chitubox)
* CBT (Chitubox)
* PHZ (Chitubox)
* PWS (Photon Workshop)
* PW0 (Photon Workshop)
* PWMX (Photon Workshop) [Not finished]
* ZCodex (Z-Suite)
* CWS (NovaMaker)
* LGS (Longer Orange 10)
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

+22
View File
@@ -0,0 +1,22 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 253.37 253.37">
<defs>
<style>
.cls-1 {
fill: #606;
}
.cls-1, .cls-2 {
fill-rule: evenodd;
}
.cls-2 {
fill: #fefefe;
stroke: #000;
stroke-miterlimit: 10;
}
</style>
</defs>
<title>UVTools</title>
<path class="cls-1" d="M43.85.87H212.78C236.1.87,255,18.52,255,40.29V214.81c0,21.77-18.9,39.43-42.22,39.43H43.85c-23.31,0-42.22-17.66-42.22-39.43V40.29C1.63,18.52,20.54.87,43.85.87Z" transform="translate(-1.63 -0.87)"/>
<path class="cls-2" d="M203.67,80a17.72,17.72,0,0,1,4.39,34.88v18.47A17.72,17.72,0,1,1,191,162.88l-16,9.24a17.48,17.48,0,0,1,.65,4.76,17.72,17.72,0,1,1-35.43,0,17.48,17.48,0,0,1,.65-4.76l-16-9.24a17.73,17.73,0,0,1-29.84-8H70.13a17.72,17.72,0,1,1-21.56-21.56V114.85a17.72,17.72,0,1,1,8.78,0v18.47a17.73,17.73,0,0,1,12.78,12.8H95a17.73,17.73,0,0,1,12.78-12.8V114.85a17.73,17.73,0,1,1,8.8,0v18.47a17.67,17.67,0,0,1,12.66,22l16,9.24a17.71,17.71,0,0,1,25.35,0l16-9.24a17.48,17.48,0,0,1-.65-4.79,17.73,17.73,0,0,1,13.32-17.16V114.85A17.72,17.72,0,0,1,203.67,80Z" transform="translate(-1.63 -0.87)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

+32
View File
@@ -0,0 +1,32 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 253.37 253.37">
<defs>
<style>
.cls-1 {
fill: #606;
}
.cls-1, .cls-2 {
fill-rule: evenodd;
}
.cls-2 {
fill: #fefefe;
}
.cls-2, .cls-3 {
stroke: #000;
stroke-miterlimit: 10;
}
.cls-3 {
font-size: 55px;
fill: #fff;
font-family: SegoeUIBlack, Segoe UI;
}
</style>
</defs>
<title>UVTools_alt</title>
<path class="cls-1" d="M43.85.87H212.78C236.1.87,255,18.52,255,40.29V214.81c0,21.77-18.9,39.43-42.22,39.43H43.85c-23.31,0-42.22-17.66-42.22-39.43V40.29C1.63,18.52,20.54.87,43.85.87Z" transform="translate(-1.63 -0.87)"/>
<path class="cls-2" d="M203.67,40a17.72,17.72,0,0,1,4.39,34.88V93.32A17.72,17.72,0,1,1,191,122.88l-16,9.24a17.48,17.48,0,0,1,.65,4.76,17.72,17.72,0,1,1-35.43,0,17.48,17.48,0,0,1,.65-4.76l-16-9.24a17.73,17.73,0,0,1-29.84-8H70.13A17.72,17.72,0,1,1,48.57,93.32V74.85a17.72,17.72,0,1,1,8.78,0V93.32a17.73,17.73,0,0,1,12.78,12.8H95a17.73,17.73,0,0,1,12.78-12.8V74.85a17.73,17.73,0,1,1,8.8,0V93.32a17.67,17.67,0,0,1,12.66,22l16,9.24a17.71,17.71,0,0,1,25.35,0l16-9.24a17.48,17.48,0,0,1-.65-4.79,17.73,17.73,0,0,1,13.32-17.16V74.85A17.72,17.72,0,0,1,203.67,40Z" transform="translate(-1.63 -0.87)"/>
<text class="cls-3" transform="translate(37.03 223.46)">TOOLS</text>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

+82 -16
View File
@@ -8,6 +8,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -27,7 +28,7 @@ namespace UVtools.Core.FileFormats
{
#region Constants
public const string GCodeStart = "G28 ; Auto Home{0}" +
public const string GCodeStart = "G28 ;Auto Home{0}" +
"G21 ;Set units to be mm{0}" +
"G91 ;Relative Positioning{0}" +
"M17 ;Enable motors{0}" +
@@ -61,7 +62,7 @@ namespace UVtools.Core.FileFormats
[DisplayName("Bottom Layers Time")] public uint BottomLayersTime { get; set; } = 35000;
[DisplayName("Number of Bottom Layers")] public ushort NumberBottomLayers { get; set; } = 3;
[DisplayName("Blanking Layer Time")] public uint BlankingLayerTime { get; set; } = 1000;
[DisplayName("BuildDirection")] public string BuildDirection { get; set; } = "Bottom_Up";
[DisplayName("Build Direction")] public string BuildDirection { get; set; } = "Bottom_Up";
[DisplayName("Lift Distance")] public float LiftDistance { get; set; } = 4;
[DisplayName("Slide/Tilt Value")] public byte TiltValue { get; set; }
[DisplayName("Use Mainlift GCode Tab")] public bool UseMainliftGCodeTab { get; set; }
@@ -148,6 +149,15 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.LightPWM,
};
public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
PrintParameterModifier.ExposureSeconds,
//PrintParameterModifier.LayerOffTime,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
PrintParameterModifier.RetractSpeed,
PrintParameterModifier.LightPWM,
};
public override byte ThumbnailsCount { get; } = 0;
public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = null;
@@ -210,7 +220,8 @@ namespace UVtools.Core.FileFormats
{
OutputSettings.LayersNum = LayerCount;
SliceSettings.LayersNum = LayerCount;
RebuildGCode();
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
@@ -416,8 +427,9 @@ namespace UVtools.Core.FileFormats
string line;
while ((line = tr.ReadLine()) != null)
{
line = line.Replace("# ", string.Empty);
if (string.IsNullOrEmpty(line)) continue;
if(line[0] == '#') continue;
//if(line[0] == '#') continue;
var splitLine = line.Split('=');
if(splitLine.Length < 2) continue;
@@ -490,10 +502,19 @@ namespace UVtools.Core.FileFormats
{
if (!zipArchiveEntry.Name.EndsWith(".png") || progress.Token.IsCancellationRequested) return;
var layerIndexStr = string.Empty;
var layerStr = zipArchiveEntry.Name.Substring(0, zipArchiveEntry.Name.Length - 4);
for (int i = layerStr.Length-1; i >= 0; i--)
{
if(layerStr[i] < '0' || layerStr[i] > '9') break;
layerIndexStr = $"{layerStr[i]}{layerIndexStr}";
}
if (string.IsNullOrEmpty(layerIndexStr)) return;
// - .png - 4 numbers
string layerStr =
zipArchiveEntry.Name.Substring(zipArchiveEntry.Name.Length - 4 - layerSize, layerSize);
uint layerIndex = uint.Parse(layerStr);
// string layerStr = zipArchiveEntry.Name.Substring(zipArchiveEntry.Name.Length - 4 - layerSize, layerSize);
uint layerIndex = uint.Parse(layerIndexStr);
var startStr = $"{GCodeKeywordSlice} {layerIndex}";
var stripGcode =
@@ -504,9 +525,44 @@ namespace UVtools.Core.FileFormats
.Trim(' ', '\n', '\r', '\t');
//var startCurrPos = stripGcode.Remove(0, ";currPos:".Length);
float liftHeight = GetInitialLayerValueOrNormal(layerIndex, BottomLiftHeight, LiftHeight);
float liftSpeed = GetInitialLayerValueOrNormal(layerIndex, BottomLiftSpeed, LiftSpeed);
float retractSpeed = RetractSpeed;
float lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, BottomLayerOffTime, LayerOffTime);
byte pwm = GetInitialLayerValueOrNormal(layerIndex, BottomLightPWM, LightPWM); ;
float exposureTime = GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime);
//var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase);
var exposureTime = Regex.Match(stripGcode, ";<Delay> (\\d+)", RegexOptions.IgnoreCase);
var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase);
var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase);
var exposureTimeRegex = Regex.Match(stripGcode, ";<Delay> (\\d+)", RegexOptions.IgnoreCase);
if (moveG1Regex.Success)
{
liftHeight = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture);
liftSpeed = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
moveG1Regex = moveG1Regex.NextMatch();
if (moveG1Regex.Success)
{
//float retractHeight = Math.Abs(float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture));
retractSpeed = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
}
}
if (pwmM106Regex.Success)
{
pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
}
if (layerIndex == 0)
{
OutputSettings.BottomLightPWM = pwm;
}
if (exposureTimeRegex.Success)
{
exposureTime = (float)Math.Round(float.Parse(exposureTimeRegex.Groups[1].Value, CultureInfo.InvariantCulture) / 1000f, 2);
}
/*var pwm = Regex.Match(stripGcode, "M106 S(\\d+)", RegexOptions.IgnoreCase);
if (layerIndex < InitialLayerCount)
{
@@ -560,7 +616,12 @@ namespace UVtools.Core.FileFormats
new Layer(layerIndex, buffer, zipArchiveEntry.Name)
{
PositionZ = GetHeightFromLayer(layerIndex),
ExposureTime = float.Parse(exposureTime.Groups[1].Value) / 1000f
LiftHeight = liftHeight,
LiftSpeed = liftSpeed,
RetractSpeed = retractSpeed,
LayerOffTime = lightOffDelay,
LightPWM = pwm,
ExposureTime = exposureTime,
};
}
else
@@ -581,7 +642,12 @@ namespace UVtools.Core.FileFormats
new Layer(layerIndex, matDecode, zipArchiveEntry.Name)
{
PositionZ = GetHeightFromLayer(layerIndex),
ExposureTime = float.Parse(exposureTime.Groups[1].Value) / 1000f
LiftHeight = liftHeight,
LiftSpeed = liftSpeed,
RetractSpeed = retractSpeed,
LayerOffTime = lightOffDelay,
LightPWM = pwm,
ExposureTime = exposureTime,
};
}
}
@@ -625,25 +691,25 @@ namespace UVtools.Core.FileFormats
{
Layer layer = this[layerIndex];
GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}");
GCode.AppendLine($"M106 S{GetInitialLayerValueOrNormal(layerIndex, OutputSettings.BottomLightPWM, OutputSettings.LightPWM)}");
GCode.AppendLine($"M106 S{layer.LightPWM}");
GCode.AppendLine($"{GCodeKeywordDelay} {layer.ExposureTime * 1000}");
GCode.AppendLine("M106 S0");
GCode.AppendLine(GCodeKeywordSliceBlank);
if (lastZPosition != layer.PositionZ)
{
if (LiftHeight > 0)
if (layer.LiftHeight > 0)
{
GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}");
GCode.AppendLine($"G1 Z-{Math.Round(LiftHeight - layer.PositionZ + lastZPosition, 2)} F{RetractSpeed}");
GCode.AppendLine($"G1 Z{layer.LiftHeight} F{layer.LiftSpeed}");
GCode.AppendLine($"G1 Z-{Math.Round(layer.LiftHeight - layer.PositionZ + lastZPosition, 2)} F{layer.RetractSpeed}");
}
else
{
GCode.AppendLine($"G1 Z{Math.Round(layer.PositionZ - lastZPosition, 2)} F{LiftSpeed}");
GCode.AppendLine($"G1 Z{Math.Round(layer.PositionZ - lastZPosition, 2)} F{layer.LiftSpeed}");
}
}
// delay = max(extra['wait'], 500) + int(((int(lift)/(extra['lift_feed']/60)) + (int(lift)/(extra['lift_retract']/60)))*1000)
uint extraDelay = (uint)(OperationCalculator.LightOffDelayC.Calculate(LiftHeight, LiftHeight, RetractSpeed) * 1000);
uint extraDelay = OperationCalculator.LightOffDelayC.CalculateMilliseconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed);
if (layerIndex < BottomLayerCount)
{
extraDelay = (uint)Math.Max(extraDelay + 10000, layer.ExposureTime * 1000);
+88 -54
View File
@@ -18,7 +18,6 @@ using System.Threading.Tasks;
using BinarySerialization;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.Operations;
@@ -26,6 +25,7 @@ namespace UVtools.Core.FileFormats
{
public class ChituboxFile : FileFormat
{
#region Constants
private const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521
private const uint MAGIC_CBT = 0x12FD0086; // 318570630
@@ -50,7 +50,7 @@ namespace UVtools.Core.FileFormats
/// <summary>
/// Gets the software version
/// </summary>
[FieldOrder(1)] public uint Version { get; set; } = 2;
[FieldOrder(1)] public uint Version { get; set; } = 3;
/// <summary>
/// Gets dimensions of the printers X output volume, in millimeters.
@@ -482,7 +482,7 @@ namespace UVtools.Core.FileFormats
/// <summary>
/// Gets how long to keep the light off after exposing this layer, in seconds.
/// </summary>
[FieldOrder(2)] public float LayerOffTimeSeconds { get; set; }
[FieldOrder(2)] public float LayerOffSeconds { get; set; }
/// <summary>
/// Gets the layer image offset to encoded layer data, and its length in bytes.
@@ -511,16 +511,7 @@ namespace UVtools.Core.FileFormats
public LayerData(ChituboxFile parent, uint layerIndex)
{
Parent = parent;
LayerPositionZ = parent[layerIndex].PositionZ;
LayerExposure = parent[layerIndex].ExposureTime;
LayerOffTimeSeconds = parent.GetInitialLayerValueOrNormal(layerIndex,
parent.PrintParametersSettings.BottomLightOffDelay,
parent.PrintParametersSettings.LightOffDelay);
/*LayerExposure = layerIndex < parent.HeaderSettings.BottomLayersCount
? parent.HeaderSettings.BottomExposureSeconds
: parent.HeaderSettings.LayerExposureSeconds;*/
RefreshLayerData(parent, layerIndex);
if (parent.HeaderSettings.Version >= 3 && Unknown2 == 0)
{
@@ -528,7 +519,13 @@ namespace UVtools.Core.FileFormats
}
}
public void RefreshLayerData(ChituboxFile parent, uint layerIndex)
{
LayerPositionZ = parent[layerIndex].PositionZ;
LayerExposure = parent[layerIndex].ExposureTime;
LayerOffSeconds = parent[layerIndex].LayerOffTime;
}
public Mat Decode(uint layerIndex, bool consumeData = true)
{
@@ -548,7 +545,7 @@ namespace UVtools.Core.FileFormats
for (byte bit = 0; bit < parent.AntiAliasing; bit++)
{
var layer = parent.LayersDefinitions[bit, layerIndex];
var layer = parent.LayerDefinitions[bit, layerIndex];
int n = 0;
for (int index = 0; index < layer.DataSize; index++)
@@ -834,8 +831,10 @@ namespace UVtools.Core.FileFormats
public override string ToString()
{
return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LayerOffTimeSeconds)}: {LayerOffTimeSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LayerOffSeconds)}: {LayerOffSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
}
}
public class LayerDataEx
@@ -870,10 +869,10 @@ namespace UVtools.Core.FileFormats
LayerData = layerData;
if (!ReferenceEquals(layerData.Parent, null))
{
LiftHeight = layerData.Parent.GetInitialLayerValueOrNormal(layerIndex, layerData.Parent.PrintParametersSettings.BottomLiftHeight, layerData.Parent.PrintParametersSettings.LiftHeight);
LiftSpeed = layerData.Parent.GetInitialLayerValueOrNormal(layerIndex, layerData.Parent.PrintParametersSettings.BottomLiftSpeed, layerData.Parent.PrintParametersSettings.LiftSpeed);
RetractSpeed = layerData.Parent.PrintParametersSettings.RetractSpeed;
LightPWM = layerData.Parent.GetInitialLayerValueOrNormal(layerIndex, layerData.Parent.HeaderSettings.BottomLightPWM, layerData.Parent.HeaderSettings.LightPWM);
LiftHeight = layerData.Parent[layerIndex].LiftHeight;
LiftSpeed = layerData.Parent[layerIndex].LiftSpeed;
RetractSpeed = layerData.Parent[layerIndex].RetractSpeed;
LightPWM = layerData.Parent[layerIndex].LightPWM;
}
if (layerData.DataSize > 0)
@@ -951,7 +950,7 @@ namespace UVtools.Core.FileFormats
public Preview[] Previews { get; protected internal set; }
public LayerData[,] LayersDefinitions { get; private set; }
public LayerData[,] LayerDefinitions { get; private set; }
public Dictionary<string, LayerData> LayersHash { get; } = new Dictionary<string, LayerData>();
@@ -992,6 +991,34 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.LightPWM,
};
public override PrintParameterModifier[] PrintParameterPerLayerModifiers {
get
{
if (HeaderSettings.Version >= 3)
{
return new[]
{
PrintParameterModifier.ExposureSeconds,
PrintParameterModifier.LayerOffTime,
PrintParameterModifier.LightPWM,
};
}
if (HeaderSettings.Version >= 2)
{
return new[]
{
PrintParameterModifier.ExposureSeconds,
PrintParameterModifier.LayerOffTime,
};
}
return null;
}
}
public override byte ThumbnailsCount { get; } = 2;
public override Size[] ThumbnailsOriginalSize { get; } = {new Size(400, 300), new Size(200, 125)};
@@ -1053,6 +1080,8 @@ namespace UVtools.Core.FileFormats
set
{
HeaderSettings.LayerCount = LayerCount;
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
HeaderSettings.OverallHeightMilimeter = TotalHeight;
}
}
@@ -1209,7 +1238,7 @@ namespace UVtools.Core.FileFormats
Previews[i] = new Preview();
}
LayersDefinitions = null;
LayerDefinitions = null;
}
public override void Encode(string fileFullPath, OperationProgress progress = null)
@@ -1248,11 +1277,12 @@ namespace UVtools.Core.FileFormats
}
else
{
HeaderSettings.Version = 2;
HeaderSettings.EncryptionKey = 0;
}
uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
LayersDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
LayerDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write))
{
@@ -1321,7 +1351,7 @@ namespace UVtools.Core.FileFormats
using (var image = this[layerIndex].LayerMat)
{
layerData.Encode(image, aaIndex, (uint) layerIndex);
LayersDefinitions[aaIndex, layerIndex] = layerData;
LayerDefinitions[aaIndex, layerIndex] = layerData;
}
lock (progress.Mutex)
@@ -1333,7 +1363,7 @@ namespace UVtools.Core.FileFormats
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
progress.Token.ThrowIfCancellationRequested();
var layerData = LayersDefinitions[aaIndex, layerIndex];
var layerData = LayerDefinitions[aaIndex, layerIndex];
LayerData layerDataHash = null;
if (!IsCbtFile /*&& HeaderSettings.EncryptionKey == 0*/)
@@ -1464,7 +1494,8 @@ namespace UVtools.Core.FileFormats
Debug.WriteLine($"{nameof(MachineName)}: {MachineName}");*/
//}
LayersDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
LayerDefinitions = new LayerData[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
var LayerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDataEx[HeaderSettings.LayerCount] : null;
uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
@@ -1479,7 +1510,7 @@ namespace UVtools.Core.FileFormats
inputFile.Seek(layerOffset, SeekOrigin.Begin);
LayerData layerData = Helpers.Deserialize<LayerData>(inputFile);
layerData.Parent = this;
LayersDefinitions[aaIndex, layerIndex] = layerData;
LayerDefinitions[aaIndex, layerIndex] = layerData;
layerOffset += (uint) Helpers.Serializer.SizeOf(layerData);
Debug.Write($"LAYER {layerIndex} -> ");
@@ -1495,9 +1526,9 @@ namespace UVtools.Core.FileFormats
else
{
inputFile.Seek(layerData.DataAddress - 84, SeekOrigin.Begin);
LayerDataEx layerDataEx = Helpers.Deserialize<LayerDataEx>(inputFile);
LayerDefinitionsEx[layerIndex] = Helpers.Deserialize<LayerDataEx>(inputFile);
Debug.Write($"LAYER {layerIndex} -> ");
Debug.WriteLine(layerDataEx);
Debug.WriteLine(LayerDefinitionsEx[layerIndex]);
}
@@ -1520,14 +1551,25 @@ namespace UVtools.Core.FileFormats
return;
}
using (var image = LayersDefinitions[0, layerIndex].Decode((uint) layerIndex))
using (var image = LayerDefinitions[0, layerIndex].Decode((uint) layerIndex))
{
this[layerIndex] = new Layer((uint) layerIndex, image)
var layer = new Layer((uint) layerIndex, image)
{
PositionZ = LayersDefinitions[0, layerIndex].LayerPositionZ,
ExposureTime = LayersDefinitions[0, layerIndex].LayerExposure
PositionZ = LayerDefinitions[0, layerIndex].LayerPositionZ,
ExposureTime = LayerDefinitions[0, layerIndex].LayerExposure,
LayerOffTime = LayerDefinitions[0, layerIndex].LayerOffSeconds,
};
if (!(LayerDefinitionsEx is null))
{
layer.LiftHeight = LayerDefinitionsEx[layerIndex].LiftHeight;
layer.LiftSpeed = LayerDefinitionsEx[layerIndex].LiftSpeed;
layer.RetractSpeed = LayerDefinitionsEx[layerIndex].RetractSpeed;
layer.LightPWM = (byte) LayerDefinitionsEx[layerIndex].LightPWM;
}
this[layerIndex] = layer;
lock (progress.Mutex)
{
progress++;
@@ -1576,13 +1618,23 @@ namespace UVtools.Core.FileFormats
{
for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
LayerDefinitions[aaIndex, layerIndex].RefreshLayerData(this, layerIndex);
outputFile.Seek(layerOffset, SeekOrigin.Begin);
layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[aaIndex, layerIndex]);
layerOffset +=
Helpers.SerializeWriteFileStream(outputFile, LayerDefinitions[aaIndex, layerIndex]);
}
}
if (HeaderSettings.Version >= 3)
{
for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
outputFile.Seek(LayerDefinitions[0, layerIndex].DataAddress - 84, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, new LayerDataEx(LayerDefinitions[0, layerIndex], layerIndex));
}
}
}
//Decode(FileFullPath, progress);
}
public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null)
@@ -1961,24 +2013,6 @@ namespace UVtools.Core.FileFormats
return false;
}
public override byte SetValuesFromPrintParametersModifiers()
{
var count = base.SetValuesFromPrintParametersModifiers();
if (count == 0) return 0;
for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
// Bottom : others
LayersDefinitions[aaIndex, layerIndex].LayerExposure = this[layerIndex].ExposureTime;
LayersDefinitions[aaIndex, layerIndex].LayerOffTimeSeconds = GetInitialLayerValueOrNormal(layerIndex, PrintParametersSettings.BottomLightOffDelay, HeaderSettings.LayerOffTime);
}
}
return count;
}
#endregion
}
}
+90 -35
View File
@@ -14,7 +14,6 @@ using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@@ -123,6 +122,15 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.LightPWM,
};
public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
PrintParameterModifier.ExposureSeconds,
PrintParameterModifier.LayerOffTime,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
PrintParameterModifier.RetractSpeed,
PrintParameterModifier.LightPWM,
};
public override byte ThumbnailsCount { get; } = 2;
public override Size[] ThumbnailsOriginalSize { get; } = {new Size(954, 850), new Size(168, 150)};
@@ -184,7 +192,8 @@ namespace UVtools.Core.FileFormats
set
{
HeaderSettings.LayerCount = LayerCount;
RebuildGCode();
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
@@ -463,28 +472,80 @@ namespace UVtools.Core.FileFormats
stripGcode = stripGcode.Substring(0, stripGcode.IndexOf(";LAYER_END")).Trim(' ', '\n', '\r', '\t');
//var startCurrPos = stripGcode.Remove(0, ";currPos:".Length);
var currPos = Regex.Match(stripGcode, ";currPos:([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase);
var exposureTime = Regex.Match(stripGcode, "G4 P(\\d+)", RegexOptions.IgnoreCase);
var pwm = Regex.Match(stripGcode, "M106 S(\\d+)", RegexOptions.IgnoreCase);
if (layerIndex < BottomLayerCount)
float posZ;
float liftHeight = GetInitialLayerValueOrNormal(layerIndex, BottomLiftHeight, LiftHeight);
float liftSpeed = GetInitialLayerValueOrNormal(layerIndex, BottomLiftSpeed, LiftSpeed);
float retractSpeed = RetractSpeed;
float lightOffDelay = 0;
byte pwm = GetInitialLayerValueOrNormal(layerIndex, BottomLightPWM, LightPWM); ;
float exposureTime = GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime);
var currPosRegex = Regex.Match(stripGcode, @";currPos:([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase);
var moveG0Regex = Regex.Match(stripGcode, @"G0 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase);
var waitG4Regex = Regex.Match(stripGcode, @"G4 P(\d+)", RegexOptions.IgnoreCase);
var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase);
if (currPosRegex.Success)
{
HeaderSettings.BottomLightPWM = byte.Parse(pwm.Groups[1].Value);
var posZRegex = currPosRegex.Groups[1].Value;
posZ = float.Parse(posZRegex, CultureInfo.InvariantCulture);
}
else
{
HeaderSettings.LightPWM = byte.Parse(pwm.Groups[1].Value);
posZ = GetHeightFromLayer(layerIndex);
}
var asd = exposureTime.NextMatch();
var asd1 = currPos.Groups[1].Value;
var asd2 = exposureTime.NextMatch().Groups[1].Value;
var posZ = float.Parse(asd1, CultureInfo.InvariantCulture);
var exp = float.Parse(asd2, CultureInfo.InvariantCulture) / 1000f;
if (moveG0Regex.Success)
{
float liftHeightTemp = float.Parse(moveG0Regex.Groups[1].Value, CultureInfo.InvariantCulture);
float liftSpeedTemp = float.Parse(moveG0Regex.Groups[3].Value, CultureInfo.InvariantCulture);
moveG0Regex = moveG0Regex.NextMatch();
if (moveG0Regex.Success)
{
float retractHeight = float.Parse(moveG0Regex.Groups[1].Value, CultureInfo.InvariantCulture);
retractSpeed = float.Parse(moveG0Regex.Groups[3].Value, CultureInfo.InvariantCulture);
liftHeight = (float) Math.Round(liftHeightTemp - retractHeight, 2);
liftSpeed = liftSpeedTemp;
}
}
if (pwmM106Regex.Success)
{
pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
}
if (layerIndex == 0)
{
HeaderSettings.BottomLightPWM = pwm;
}
/*else if(layerIndex)
{
HeaderSettings.LightPWM = byte.Parse(pwmM106Regex.Groups[1].Value);
}*/
if (waitG4Regex.Success)
{
lightOffDelay = (float) Math.Round(float.Parse(waitG4Regex.Groups[1].Value, CultureInfo.InvariantCulture) / 1000f, 2);
waitG4Regex = waitG4Regex.NextMatch();
if (waitG4Regex.Success)
{
exposureTime = (float) Math.Round(float.Parse(waitG4Regex.Groups[1].Value, CultureInfo.InvariantCulture) / 1000f, 2);
}
else // Only one match, meaning light off delay is not present
{
lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, BottomLayerOffTime, LayerOffTime);
}
}
LayerManager[layerIndex] = new Layer(layerIndex, entry.Open(), entry.Name)
{
PositionZ = posZ,
ExposureTime = exp
LiftHeight = liftHeight,
LiftSpeed = liftSpeed,
RetractSpeed = retractSpeed,
LayerOffTime = lightOffDelay,
LightPWM = pwm,
ExposureTime = exposureTime,
};
progress++;
}
@@ -543,43 +604,38 @@ namespace UVtools.Core.FileFormats
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
var liftHeight = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLiftHeight,
HeaderSettings.LiftHeight);
float liftZHeight = (float)Math.Round(liftHeight + this[layerIndex].PositionZ, 2);
var liftZSpeed = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLiftSpeed,
HeaderSettings.LiftSpeed);
var lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightOffTime,
HeaderSettings.LightOffTime) * 1000;
var pwmValue = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightPWM, HeaderSettings.LightPWM);
var exposureTime = this[layerIndex].ExposureTime * 1000;
var layer = this[layerIndex];
var exposureTime = layer.ExposureTime * 1000;
var liftHeight = layer.LiftHeight;
var liftZHeight = Math.Round(liftHeight + layer.PositionZ, 2);
var liftSpeed = layer.LiftSpeed;
var retractSpeed = layer.RetractSpeed;
var lightOffDelay = layer.LayerOffTime * 1000;
var pwmValue = layer.LightPWM;
GCode.AppendLine($";LAYER_START:{layerIndex}");
GCode.AppendLine($";currPos:{this[layerIndex].PositionZ}");
GCode.AppendLine($";currPos:{layer.PositionZ}");
GCode.AppendLine($"M6054 \"{layerIndex + 1}.png\";show Image");
// Absolute gcode
if (liftHeight > 0 && liftZHeight > this[layerIndex].PositionZ)
if (liftHeight > 0 && liftZHeight > layer.PositionZ)
{
GCode.AppendLine($"G0 Z{liftZHeight} F{liftZSpeed};Z Lift");
GCode.AppendLine($"G0 Z{liftZHeight} F{liftSpeed};Z Lift");
}
if (lastZPosition < this[layerIndex].PositionZ)
if (lastZPosition < layer.PositionZ)
{
GCode.AppendLine($"G0 Z{this[layerIndex].PositionZ} F{HeaderSettings.RetractSpeed};Layer position");
GCode.AppendLine($"G0 Z{layer.PositionZ} F{retractSpeed};Layer position");
}
GCode.AppendLine($"G4 P{lightOffDelay};Before cure delay");
GCode.AppendLine($"G4 P{lightOffDelay};Stabilization delay");
GCode.AppendLine($"M106 S{pwmValue};light on");
GCode.AppendLine($"G4 P{exposureTime};Cure time");
GCode.AppendLine("M106 S0;light off");
GCode.AppendLine(";LAYER_END");
GCode.AppendLine();
lastZPosition = this[layerIndex].PositionZ;
lastZPosition = layer.PositionZ;
}
GCode.AppendFormat(GCodeEnd, Environment.NewLine, HeaderSettings.MachineZ);
@@ -634,7 +690,6 @@ namespace UVtools.Core.FileFormats
HeaderSettings
=
{
Version = 2,
BedSizeX = HeaderSettings.MachineX,
BedSizeY = HeaderSettings.MachineY,
BedSizeZ = HeaderSettings.MachineZ,
+106 -13
View File
@@ -7,8 +7,11 @@
*/
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
@@ -180,6 +183,7 @@ namespace UVtools.Core.FileFormats
new SL1File(), // Prusa SL1
new ChituboxZipFile(), // Zip
new ChituboxFile(), // cbddlp, cbt, photon
new PhotonSFile(), // photons
new PHZFile(), // phz
new PWSFile(), // PSW
new ZCodexFile(), // zcodex
@@ -274,6 +278,7 @@ namespace UVtools.Core.FileFormats
public abstract Type[] ConvertToFormats { get; }
public abstract PrintParameterModifier[] PrintParameterModifiers { get; }
public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = null;
public string FileFilter {
get
@@ -387,6 +392,7 @@ namespace UVtools.Core.FileFormats
public float TotalHeight => LayerCount == 0 ? 0 : this[LayerCount - 1].PositionZ; //(float)Math.Round(LayerCount * LayerHeight, 2);
public uint LastLayerIndex => LayerCount - 1;
public virtual bool SupportPerLayerSettings => !(PrintParameterPerLayerModifiers is null || PrintParameterPerLayerModifiers.Length == 0);
public virtual uint LayerCount
{
@@ -395,6 +401,7 @@ namespace UVtools.Core.FileFormats
}
public virtual ushort BottomLayerCount { get; set; }
public uint NormalLayerCount => LayerCount - BottomLayerCount;
public virtual float BottomExposureTime { get; set; }
public virtual float ExposureTime { get; set; }
public virtual float BottomLayerOffTime { get; set; }
@@ -409,8 +416,20 @@ namespace UVtools.Core.FileFormats
public abstract float PrintTime { get; }
//(header.numberOfLayers - header.bottomLayers) * (double) header.exposureTimeSeconds + (double) header.bottomLayers * (double) header.exposureBottomTimeSeconds + (double) header.offTimeSeconds * (double) header.numberOfLayers);
public virtual float PrintTimeOrComputed
{
get
{
if (PrintTime > 0) return PrintTime;
return NormalLayerCount * ExposureTime +
NormalLayerCount * LayerOffTime +
BottomLayerCount * BottomExposureTime +
NormalLayerCount * BottomLayerOffTime;
}
}
public float PrintTimeHours => (float) Math.Round(PrintTime / 3600, 2);
public float PrintTimeHours => (float) Math.Round(PrintTimeOrComputed / 3600, 2);
public abstract float UsedMaterial { get; }
@@ -434,7 +453,39 @@ namespace UVtools.Core.FileFormats
protected FileFormat()
{
Thumbnails = new Mat[ThumbnailsCount];
PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(e.PropertyName);
if (e.PropertyName == nameof(LayerCount))
{
LayerManager.RebuildLayersProperties();
RebuildGCode();
return;
}
if (
e.PropertyName == nameof(BottomLayerCount) ||
e.PropertyName == nameof(BottomExposureTime) ||
e.PropertyName == nameof(ExposureTime) ||
e.PropertyName == nameof(BottomLayerOffTime) ||
e.PropertyName == nameof(LayerOffTime) ||
e.PropertyName == nameof(BottomLiftHeight) ||
e.PropertyName == nameof(LiftHeight) ||
e.PropertyName == nameof(BottomLiftSpeed) ||
e.PropertyName == nameof(LiftSpeed) ||
e.PropertyName == nameof(RetractSpeed) ||
e.PropertyName == nameof(BottomLightPWM) ||
e.PropertyName == nameof(LightPWM)
)
{
LayerManager.RebuildLayersProperties(false);
RebuildGCode();
return;
}
}
#endregion
#region Indexers
@@ -828,6 +879,42 @@ namespace UVtools.Core.FileFormats
}
}
public void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex)
{
if (PrintParameterPerLayerModifiers is null) return;
var layer = this[layerIndex];
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.ExposureSeconds))
{
PrintParameterModifier.ExposureSeconds.OldValue = (decimal)layer.ExposureTime;
}
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LayerOffTime))
{
PrintParameterModifier.LayerOffTime.OldValue = (decimal)layer.LayerOffTime;
}
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight))
{
PrintParameterModifier.LiftHeight.OldValue = (decimal)layer.LiftHeight;
}
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed))
{
PrintParameterModifier.LiftSpeed.OldValue = (decimal)layer.LiftSpeed;
}
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed))
{
PrintParameterModifier.RetractSpeed.OldValue = (decimal)layer.RetractSpeed;
}
if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightPWM))
{
PrintParameterModifier.LightPWM.OldValue = layer.LightPWM;
}
}
public object GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
{
if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount))
@@ -930,7 +1017,7 @@ namespace UVtools.Core.FileFormats
return false;
}
public virtual byte SetValuesFromPrintParametersModifiers()
public byte SetValuesFromPrintParametersModifiers()
{
if (PrintParameterModifiers is null) return 0;
byte changed = 0;
@@ -942,20 +1029,26 @@ namespace UVtools.Core.FileFormats
changed++;
}
if (changed == 0) return changed;
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
this[layerIndex].ExposureTime = GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime);
}
RebuildGCode();
return changed;
}
public virtual void RebuildGCode()
{ }
public void EditPrintParameters(OperationEditParameters operation)
{
if (operation.PerLayerOverride)
{
for (uint layerIndex = operation.LayerIndexStart; layerIndex <= operation.LayerIndexEnd; layerIndex++)
{
this[layerIndex].SetValuesFromPrintParametersModifiers(operation.Modifiers);
}
RebuildGCode();
}
else
{
SetValuesFromPrintParametersModifiers();
}
}
public virtual void RebuildGCode() { }
public void Save(OperationProgress progress = null)
{
+25 -4
View File
@@ -40,6 +40,11 @@ namespace UVtools.Core.FileFormats
/// </summary>
FileFormat.PrintParameterModifier[] PrintParameterModifiers { get; }
/// <summary>
/// Gets the available <see cref="FileFormat.PrintParameterModifier"/> per layer
/// </summary>
FileFormat.PrintParameterModifier[] PrintParameterPerLayerModifiers { get; }
/// <summary>
/// Gets the file filter for open and save dialogs
/// </summary>
@@ -76,8 +81,6 @@ namespace UVtools.Core.FileFormats
/// </summary>
Mat[] Thumbnails { get; set; }
/// <summary>
/// Gets the cached layers into compressed bytes
/// </summary>
@@ -127,13 +130,18 @@ namespace UVtools.Core.FileFormats
/// </summary>
float TotalHeight { get; }
#region Universal Properties
/// <summary>
/// Gets the last layer index
/// </summary>
uint LastLayerIndex { get; }
#region Universal Properties
/// <summary>
/// Gets if this format support per layer override settings
/// </summary>
bool SupportPerLayerSettings { get; }
/// <summary>
/// Gets the number of layers present in this file
/// </summary>
@@ -144,6 +152,11 @@ namespace UVtools.Core.FileFormats
/// </summary>
ushort BottomLayerCount { get; set; }
/// <summary>
/// Gets the number of normal layer count
/// </summary>
uint NormalLayerCount { get; }
/// <summary>
/// Gets or sets the initial exposure time for <see cref="BottomLayerCount"/> in seconds
/// </summary>
@@ -205,6 +218,11 @@ namespace UVtools.Core.FileFormats
/// </summary>
float PrintTime { get; }
/// <summary>
/// Gets the estimate print time in seconds, if print doesn't support it it will be calculated
/// </summary>
float PrintTimeOrComputed { get; }
/// <summary>
/// Gets the estimate print time in hours
/// </summary>
@@ -375,6 +393,7 @@ namespace UVtools.Core.FileFormats
T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue);
void RefreshPrintParametersModifiersValues();
void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex);
/// <summary>
/// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/>
@@ -393,6 +412,8 @@ namespace UVtools.Core.FileFormats
byte SetValuesFromPrintParametersModifiers();
void EditPrintParameters(OperationEditParameters operation);
/// <summary>
/// Rebuilds GCode based on current settings
/// </summary>
+6 -1
View File
@@ -280,7 +280,12 @@ namespace UVtools.Core.FileFormats
public override uint LayerCount
{
set => HeaderSettings.LayerCount = LayerCount;
set
{
HeaderSettings.LayerCount = LayerCount;
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
public override ushort BottomLayerCount
+17 -24
View File
@@ -454,12 +454,14 @@ namespace UVtools.Core.FileFormats
public LayerData(PHZFile parent, uint layerIndex)
{
Parent = parent;
LayerPositionZ = parent[layerIndex].PositionZ;
LayerExposure = parent[layerIndex].ExposureTime;
RefreshLayerData(layerIndex);
}
LayerOffTimeSeconds = parent.GetInitialLayerValueOrNormal(layerIndex,
parent.HeaderSettings.BottomLightOffDelay,
parent.HeaderSettings.LayerOffTime);
public void RefreshLayerData(uint layerIndex)
{
LayerPositionZ = Parent[layerIndex].PositionZ;
LayerExposure = Parent[layerIndex].ExposureTime;
LayerOffTimeSeconds = Parent[layerIndex].LayerOffTime;
}
public unsafe Mat Decode(uint layerIndex, bool consumeData = true)
@@ -708,6 +710,11 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.LightPWM,
};
public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
PrintParameterModifier.ExposureSeconds,
PrintParameterModifier.LayerOffTime,
};
public override byte ThumbnailsCount { get; } = 2;
public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)};
@@ -770,6 +777,8 @@ namespace UVtools.Core.FileFormats
set
{
HeaderSettings.LayerCount = LayerCount;
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
HeaderSettings.OverallHeightMilimeter = TotalHeight;
}
}
@@ -1153,7 +1162,8 @@ namespace UVtools.Core.FileFormats
this[layerIndex] = new Layer((uint) layerIndex, image)
{
PositionZ = LayersDefinitions[layerIndex].LayerPositionZ,
ExposureTime = LayersDefinitions[layerIndex].LayerExposure
ExposureTime = LayersDefinitions[layerIndex].LayerExposure,
LayerOffTime = LayersDefinitions[layerIndex].LayerOffTimeSeconds
};
}
@@ -1200,28 +1210,12 @@ namespace UVtools.Core.FileFormats
uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
LayersDefinitions[layerIndex].RefreshLayerData(layerIndex);
outputFile.Seek(layerOffset, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]);
layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]);
}
}
//Decode(FileFullPath, progress);
}
public override byte SetValuesFromPrintParametersModifiers()
{
var count = base.SetValuesFromPrintParametersModifiers();
if (count == 0) return 0;
for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
// Bottom : others
LayersDefinitions[layerIndex].LayerExposure = this[layerIndex].ExposureTime;
LayersDefinitions[layerIndex].LayerOffTimeSeconds = GetInitialLayerValueOrNormal(layerIndex, HeaderSettings.BottomLightOffDelay, HeaderSettings.LayerOffTime);
}
return count;
}
public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null)
@@ -1234,7 +1228,6 @@ namespace UVtools.Core.FileFormats
HeaderSettings
=
{
Version = 2,
BedSizeX = HeaderSettings.BedSizeX,
BedSizeY = HeaderSettings.BedSizeY,
BedSizeZ = HeaderSettings.BedSizeZ,
+20 -13
View File
@@ -297,11 +297,6 @@ namespace UVtools.Core.FileFormats
var g = (color16 >> 5) & 0x3f;
var b = (color16 >> 0) & 0x1f;
/*span[pixel++] = new Rgba32(
(byte)((r << 3) | (r & 0x7)),
(byte)((g << 2) | (g & 0x3)),
(byte)((b << 3) | (b & 0x7))
);*/
span[pixel++] = (byte) ((b << 3) | (b & 0x7));
span[pixel++] = (byte) ((g << 2) | (g & 0x3));
span[pixel++] = (byte) ((r << 3) | (r & 0x7));
@@ -403,11 +398,15 @@ namespace UVtools.Core.FileFormats
public LayerData(PWSFile parent, uint layerIndex)
{
Parent = parent;
LiftHeight = Parent.HeaderSettings.LiftHeight;
LiftSpeed = Parent.HeaderSettings.LiftSpeed;
RefreshLayerData(layerIndex);
}
LayerPositionZ = parent[layerIndex].PositionZ;
LayerExposure = parent[layerIndex].ExposureTime;
public void RefreshLayerData(uint layerIndex)
{
LayerPositionZ = Parent[layerIndex].PositionZ;
LayerExposure = Parent[layerIndex].ExposureTime;
LiftHeight = Parent[layerIndex].LiftHeight;
LiftSpeed = Parent[layerIndex].LiftSpeed;
}
public Mat Decode(bool consumeData = true)
@@ -799,9 +798,15 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.RetractSpeed,
};
public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
PrintParameterModifier.ExposureSeconds,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
};
public override byte ThumbnailsCount { get; } = 1;
public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(224, 168)};
public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new Size(224, 168)};
public override uint ResolutionX
{
@@ -852,6 +857,7 @@ namespace UVtools.Core.FileFormats
{
LayersDefinition.LayersCount = LayerCount;
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
@@ -1145,7 +1151,9 @@ namespace UVtools.Core.FileFormats
this[layerIndex] = new Layer((uint) layerIndex, image)
{
PositionZ = LayersDefinition[(uint)layerIndex].LayerPositionZ,
ExposureTime = LayersDefinition[(uint)layerIndex].LayerExposure
ExposureTime = LayersDefinition[(uint)layerIndex].LayerExposure,
LiftHeight = LayersDefinition[(uint)layerIndex].LiftHeight,
LiftSpeed = LayersDefinition[(uint)layerIndex].LiftSpeed,
};
}
@@ -1187,11 +1195,10 @@ namespace UVtools.Core.FileFormats
outputFile.Seek(FileMarkSettings.LayerDefinitionAddress + Helpers.Serializer.SizeOf(HeaderSettings.Section) + Helpers.Serializer.SizeOf(LayersDefinition), SeekOrigin.Begin);
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
LayersDefinition[layerIndex].RefreshLayerData(layerIndex);
Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]);
}
}
//Decode(FileFullPath, progress);
}
public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null)
+567
View File
@@ -0,0 +1,567 @@
/*
* 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.
*/
// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using BinarySerialization;
using Emgu.CV;
using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Operations;
namespace UVtools.Core.FileFormats
{
public class PhotonSFile : FileFormat
{
public const byte RLEEncodingLimit = 128; // 128;
#region Sub Classes
#region Header
public class Header
{
public const uint ResolutionX = 1440;
public const uint ResolutionY = 2560;
public const float DisplayWidth = 68.04f;
public const float DisplayHeight = 120.96f;
public const float BuildZ = 150f;
public const uint TAG1 = 2;
public const ushort TAG2 = 49;
[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Tag1 { get; set; } = TAG1; // 2
[FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Tag2 { get; set; } = TAG2; // 49
[FieldOrder(2)] [FieldEndianness(Endianness.Big)] public double XYPixelSize { get; set; } = 0.04725; // 0.04725
[FieldOrder(3)] [FieldEndianness(Endianness.Big)] public double LayerHeight { get; set; }
[FieldOrder(4)] [FieldEndianness(Endianness.Big)] public double ExposureSeconds { get; set; }
[FieldOrder(5)] [FieldEndianness(Endianness.Big)] public double LayerOffSeconds { get; set; }
[FieldOrder(6)] [FieldEndianness(Endianness.Big)] public double BottomExposureSeconds { get; set; }
[FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint BottomLayerCount { get; set; }
[FieldOrder(8)] [FieldEndianness(Endianness.Big)] public double LiftHeight { get; set; } // mm
[FieldOrder(9)] [FieldEndianness(Endianness.Big)] public double LiftSpeed { get; set; } // mm/s
[FieldOrder(10)] [FieldEndianness(Endianness.Big)] public double RetractSpeed { get; set; } // mm/s
[FieldOrder(11)] [FieldEndianness(Endianness.Big)] public double Volume { get; set; } // ml
[FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 225;
[FieldOrder(13)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 42;
[FieldOrder(14)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionY { get; set; } = 168;
[FieldOrder(15)] [FieldEndianness(Endianness.Big)] public uint Unknown4 { get; set; } = 10;
public override string ToString()
{
return $"{nameof(Tag1)}: {Tag1}, {nameof(Tag2)}: {Tag2}, {nameof(XYPixelSize)}: {XYPixelSize}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureSeconds)}: {ExposureSeconds}, {nameof(LayerOffSeconds)}: {LayerOffSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(Volume)}: {Volume}, {nameof(PreviewResolutionX)}: {PreviewResolutionX}, {nameof(Unknown2)}: {Unknown2}, {nameof(PreviewResolutionY)}: {PreviewResolutionY}, {nameof(Unknown4)}: {Unknown4}";
}
}
public class LayerHeader
{
[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; }
public override string ToString()
{
return $"{nameof(LayerCount)}: {LayerCount}";
}
}
#endregion
#region LayerDef
public class LayerData
{
[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown1 { get; set; } = 44944;
[FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 0;
[FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint Unknown3 { get; set; } = 0;
[FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = 1440;
[FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = 2560;
[FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; }
[Ignore] public uint RleDataSize => (DataSize >> 3) - 4;
[FieldOrder(6)] [FieldEndianness(Endianness.Big)] public uint Unknown5 { get; set; } = 2684702720;
[Ignore] public byte[] EncodedRle { get; set; }
public override string ToString()
{
return $"{nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DataSize)}: {DataSize}, {nameof(RleDataSize)}: {RleDataSize}, {nameof(Unknown5)}: {Unknown5}, {nameof(EncodedRle)}: {EncodedRle.Length}";
}
public unsafe byte[] Encode(Mat mat)
{
List<byte> rawData = new List<byte>();
List<byte> chunk = new List<byte>();
var spanMat = mat.GetBytePointer();
var imageLength = mat.GetLength();
EncodedRle = rawData.ToArray();
DataSize = (uint)(EncodedRle.Length * 8 + 32);
return EncodedRle;
}
public unsafe Mat Decode(bool consumeRle = true)
{
var mat = EmguExtensions.InitMat(new Size((int) ResolutionX, (int) ResolutionY));
var matSpan = mat.GetBytePointer();
var imageLength = mat.GetLength();
int pixel = 0;
foreach (var run in EncodedRle)
{
byte col = (byte) ((run & 0x01) * 255);
var numPixelsInRun =
(((run & 128) > 0 ? 1 : 0) |
((run & 64) > 0 ? 2 : 0) |
((run & 32) > 0 ? 4 : 0) |
((run & 16) > 0 ? 8 : 0) |
((run & 8) > 0 ? 16 : 0) |
((run & 4) > 0 ? 32 : 0) |
((run & 2) > 0 ? 64 : 0)) + 1;
for (; numPixelsInRun > 0; numPixelsInRun--)
{
if (pixel > imageLength)
{
mat.Dispose();
throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels");
}
matSpan[pixel++] = col;
}
}
// Not required as mat is all black by default
//for (;pixel < imageLength; pixel++) matSpan[pixel] = 0;
if (consumeRle)
EncodedRle = null;
return mat;
}
}
#endregion
#endregion
#region Properties
public Header HeaderSettings { get; protected internal set; } = new Header();
public LayerHeader LayerSettings { get; protected internal set; } = new LayerHeader();
public override FileFormatType FileType => FileFormatType.Binary;
public override FileExtension[] FileExtensions { get; } = {
new FileExtension("photons", "Chitubox PhotonS Files"),
};
public override Type[] ConvertToFormats { get; } =
{
//typeof(UVJFile),
};
public override PrintParameterModifier[] PrintParameterModifiers { get; } =
{
PrintParameterModifier.BottomLayerCount,
PrintParameterModifier.BottomExposureSeconds,
PrintParameterModifier.ExposureSeconds,
//PrintParameterModifier.BottomLayerOffTime,
PrintParameterModifier.LayerOffTime,
//PrintParameterModifier.BottomLiftHeight,
//PrintParameterModifier.BottomLiftSpeed,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
PrintParameterModifier.RetractSpeed,
};
public override byte ThumbnailsCount { get; } = 1;
public override Size[] ThumbnailsOriginalSize { get; } = {new Size(225, 168) };
public override uint ResolutionX
{
get => Header.ResolutionX;
set
{
}
}
public override uint ResolutionY
{
get => Header.ResolutionY;
set
{
}
}
public override float DisplayWidth
{
get => Header.DisplayWidth;
set { }
}
public override float DisplayHeight
{
get => Header.DisplayHeight;
set { }
}
public override byte AntiAliasing => 1;
public override float LayerHeight
{
get => (float) Math.Round(HeaderSettings.LayerHeight);
set
{
HeaderSettings.LayerHeight = value;
RaisePropertyChanged();
}
}
public override uint LayerCount
{
set
{
LayerSettings.LayerCount = LayerCount;
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
public override ushort BottomLayerCount
{
get => (ushort) HeaderSettings.BottomLayerCount;
set
{
HeaderSettings.BottomLayerCount = value;
RaisePropertyChanged();
}
}
public override float BottomExposureTime
{
get => (float) HeaderSettings.BottomExposureSeconds;
set
{
HeaderSettings.BottomExposureSeconds = value;
RaisePropertyChanged();
}
}
public override float ExposureTime
{
get => (float)HeaderSettings.ExposureSeconds;
set
{
HeaderSettings.ExposureSeconds = value;
RaisePropertyChanged();
}
}
/*public override float BottomLayerOffTime
{
get => HeaderSettings.BottomLightOffDelayMs;
set
{
HeaderSettings.BottomLightOffDelayMs = value;
RaisePropertyChanged();
}
}*/
public override float LayerOffTime
{
get => (float) HeaderSettings.LayerOffSeconds;
set
{
HeaderSettings.LayerOffSeconds = value;
RaisePropertyChanged();
}
}
/*public override float BottomLiftHeight
{
get => HeaderSettings.BottomLiftHeight;
set
{
HeaderSettings.BottomLiftHeight = value;
RaisePropertyChanged();
}
}*/
public override float LiftHeight
{
get => (float) HeaderSettings.LiftHeight;
set
{
HeaderSettings.LiftHeight = value;
RaisePropertyChanged();
}
}
/*public override float BottomLiftSpeed
{
get => HeaderSettings.BottomLiftSpeed;
set
{
HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value;
RaisePropertyChanged();
}
}*/
public override float LiftSpeed
{
get => (float) Math.Round(HeaderSettings.LiftSpeed * 60.0);
set
{
HeaderSettings.LiftSpeed = Math.Round(value / 60.0);
RaisePropertyChanged();
}
}
public override float RetractSpeed
{
get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0);
set
{
HeaderSettings.RetractSpeed = Math.Round(value / 60.0);
RaisePropertyChanged();
}
}
public override float PrintTime => 0;
public override float UsedMaterial => (float) HeaderSettings.Volume;
public override float MaterialCost => 0;
public override string MaterialName => "Unknown";
public override string MachineName => "Anycubic Photon S";
public override object[] Configs => new object[] { HeaderSettings };
#endregion
#region Constructors
public PhotonSFile()
{
}
#endregion
#region Methods
public unsafe byte[] PreviewEncode(Mat mat)
{
byte[] bytes = new byte[mat.Width * mat.Height * 2];
var span = mat.GetBytePointer();
var imageLength = mat.GetLength();
int index = 0;
for (int i = 0; i < imageLength; i+=3)
{
byte b = span[i];
byte g = span[i+1];
byte r = span[i+2];
ushort rgb15 = (ushort) (((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 0));
bytes[index++] = (byte) (rgb15 >> 8);
bytes[index++] = (byte) (rgb15 & 0xff);
}
if (index != bytes.Length)
{
throw new FileLoadException($"Preview encode incomplete encode, expected: {bytes.Length}, encoded: {index}");
}
return bytes;
}
public override void Encode(string fileFullPath, OperationProgress progress = null)
{
base.Encode(fileFullPath, progress);
//uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write))
{
outputFile.WriteSerialize(HeaderSettings);
outputFile.WriteBytes(PreviewEncode(Thumbnails[0]));
outputFile.WriteSerialize(LayerSettings);
LayerData[] layerData = new LayerData[LayerCount];
Parallel.For(0, LayerCount, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
using (var mat = this[layerIndex].LayerMat)
{
layerData[layerIndex] = new LayerData();
layerData[layerIndex].Encode(mat);
}
lock (progress.Mutex)
{
progress++;
}
});
progress.ItemName = "Saving layers";
progress.ProcessedItems = 0;
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
progress.Token.ThrowIfCancellationRequested();
outputFile.WriteSerialize(layerData[layerIndex]);
outputFile.WriteBytes(layerData[layerIndex].EncodedRle);
progress++;
}
}
AfterEncode();
Debug.WriteLine("Encode Results:");
Debug.WriteLine(HeaderSettings);
Debug.WriteLine("-End-");
}
public unsafe Mat PreviewDecode(byte []data)
{
Mat mat = new Mat((int) HeaderSettings.PreviewResolutionX, (int)HeaderSettings.PreviewResolutionY, DepthType.Cv8U, 3);
var span = mat.GetBytePointer();
int spanIndex = 0;
for (int i = 0; i < data.Length; i+=2)
{
ushort color16 = (ushort)(data[i] + (data[i + 1] << 8));
var r = (color16 >> 11) & 0x1F;
var g = (color16 >> 5) & 0x3F;
var b = (color16 >> 0) & 0x1F;
/*span[spanIndex++] = (byte)(b << 3);
span[spanIndex++] = (byte)(g << 2);
span[spanIndex++] = (byte)(r << 3);*/
span[spanIndex++] = (byte)((b << 3) | (b & 0x7));
span[spanIndex++] = (byte)((g << 2) | (g & 0x3));
span[spanIndex++] = (byte)((r << 3) | (r & 0x7));
}
return mat;
}
public override void Decode(string fileFullPath, OperationProgress progress = null)
{
base.Decode(fileFullPath, progress);
using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read))
{
HeaderSettings = Helpers.Deserialize<Header>(inputFile);
if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2)
{
throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", fileFullPath);
}
HeaderSettings.LayerHeight = Math.Round(HeaderSettings.LayerHeight, 2);
HeaderSettings.Volume = Math.Round(HeaderSettings.Volume, 2);
int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2);
byte[] previewData = new byte[previewSize];
uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings);
currentOffset += inputFile.ReadBytes(previewData);
Thumbnails[0] = PreviewDecode(previewData);
LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile);
currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings);
Debug.WriteLine(HeaderSettings);
Debug.WriteLine(LayerSettings);
LayerData[] layerData = new LayerData[LayerSettings.LayerCount];
progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount);
for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++)
{
progress.Token.ThrowIfCancellationRequested();
layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile);
layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize];
currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle);
Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}");
}
LayerManager = new LayerManager(LayerSettings.LayerCount, this);
progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
Parallel.For(0, LayerCount,
//new ParallelOptions{MaxDegreeOfParallelism = 1},
layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
using (var image = layerData[layerIndex].Decode())
{
this[layerIndex] = new Layer((uint) layerIndex, image);
lock (progress.Mutex)
{
progress++;
}
}
});
LayerManager.RebuildLayersProperties();
FileFullPath = fileFullPath;
}
progress.Token.ThrowIfCancellationRequested();
}
public override void SaveAs(string filePath = null, OperationProgress progress = null)
{
if (RequireFullEncode)
{
if (!string.IsNullOrEmpty(filePath))
{
FileFullPath = filePath;
}
Encode(FileFullPath, progress);
return;
}
if (!string.IsNullOrEmpty(filePath))
{
File.Copy(FileFullPath, filePath, true);
FileFullPath = filePath;
}
using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write))
{
outputFile.Seek(0, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
}
}
public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null)
{
return false;
}
#endregion
}
}
-1
View File
@@ -716,7 +716,6 @@ namespace UVtools.Core.FileFormats
LayerManager = LayerManager,
HeaderSettings =
{
Version = 2,
BedSizeX = PrinterSettings.DisplayWidth,
BedSizeY = PrinterSettings.DisplayHeight,
BedSizeZ = PrinterSettings.MaxPrintHeight,
+26 -47
View File
@@ -198,6 +198,8 @@ namespace UVtools.Core.FileFormats
public override byte AntiAliasing => JsonSettings.Properties.AntiAliasLevel;
public override bool SupportPerLayerSettings => true;
public override float LayerHeight
{
get => JsonSettings.Properties.Size.LayerHeight;
@@ -213,8 +215,8 @@ namespace UVtools.Core.FileFormats
set
{
JsonSettings.Properties.Size.Layers = LayerCount;
JsonSettings.Layers.Clear();
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
@@ -363,24 +365,25 @@ namespace UVtools.Core.FileFormats
{
base.Encode(fileFullPath, progress);
if (JsonSettings.Layers.Count == 0)
// Redo layer data
JsonSettings.Layers.Clear();
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
var layer = this[layerIndex];
JsonSettings.Layers.Add(new LayerData
{
JsonSettings.Layers.Add(new LayerData
Z = layer.PositionZ,
Exposure = new Exposure
{
Z = this[layerIndex].PositionZ,
Exposure = new Exposure
{
LiftHeight = GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LiftHeight, JsonSettings.Properties.Exposure.LiftHeight),
LiftSpeed = GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LiftSpeed, JsonSettings.Properties.Exposure.LiftSpeed),
LightOnTime = GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime),
LightOffTime = GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LightOffTime, JsonSettings.Properties.Exposure.LightOffTime),
LightPWM = GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LightPWM, JsonSettings.Properties.Exposure.LightPWM),
RetractSpeed = GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.RetractSpeed, JsonSettings.Properties.Exposure.RetractSpeed),
}
});
}
LiftHeight = layer.LiftHeight,
LiftSpeed = layer.LiftSpeed,
RetractHeight = layer.LiftHeight+1,
RetractSpeed = layer.RetractSpeed,
LightOffTime = layer.LayerOffTime,
LightOnTime = layer.ExposureTime,
LightPWM = layer.LightPWM
}
});
}
using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create))
@@ -445,7 +448,7 @@ namespace UVtools.Core.FileFormats
}
JsonSettings = Helpers.JsonDeserializeObject<Settings>(entry.Open());
LayerManager = new LayerManager(JsonSettings.Properties.Size.Layers, this);
entry = inputFile.GetEntry(FilePreviewTinyName);
@@ -476,7 +479,12 @@ namespace UVtools.Core.FileFormats
LayerManager[layerIndex] = new Layer(layerIndex, entry.Open(), entry.Name)
{
PositionZ = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int) layerIndex].Z : GetHeightFromLayer(layerIndex),
ExposureTime = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LightOnTime : GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime)
LiftHeight = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LiftHeight : GetInitialLayerValueOrNormal(layerIndex, BottomLiftHeight, LiftHeight),
LiftSpeed = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LiftSpeed : GetInitialLayerValueOrNormal(layerIndex, BottomLiftSpeed, LiftSpeed),
RetractSpeed = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.RetractSpeed : RetractSpeed,
LayerOffTime = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LightOffTime : GetInitialLayerValueOrNormal(layerIndex, BottomLayerOffTime, LayerOffTime),
ExposureTime = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LightOnTime : GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime),
LightPWM = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LightPWM : GetInitialLayerValueOrNormal(layerIndex, BottomLightPWM, LightPWM),
};
}
@@ -486,35 +494,6 @@ namespace UVtools.Core.FileFormats
LayerManager.GetBoundingRectangle(progress);
}
public override byte SetValuesFromPrintParametersModifiers()
{
var count = base.SetValuesFromPrintParametersModifiers();
if (count == 0) return 0;
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
// Bottom : others
if (JsonSettings.Layers.Count <= layerIndex) break;
JsonSettings.Layers[(int)layerIndex].Exposure.LiftHeight =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LiftHeight, JsonSettings.Properties.Exposure.LiftHeight);
JsonSettings.Layers[(int)layerIndex].Exposure.LightPWM =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LightPWM, JsonSettings.Properties.Exposure.LightPWM);
JsonSettings.Layers[(int)layerIndex].Exposure.LiftSpeed =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LiftSpeed, JsonSettings.Properties.Exposure.LiftSpeed);
JsonSettings.Layers[(int)layerIndex].Exposure.LightOnTime =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LightOnTime, JsonSettings.Properties.Exposure.LightOnTime);
JsonSettings.Layers[(int)layerIndex].Exposure.RetractSpeed =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.RetractSpeed, JsonSettings.Properties.Exposure.RetractSpeed);
JsonSettings.Layers[(int)layerIndex].Exposure.LightOffTime =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.LightOffTime, JsonSettings.Properties.Exposure.LightOffTime);
JsonSettings.Layers[(int)layerIndex].Exposure.RetractHeight =
GetInitialLayerValueOrNormal(layerIndex, JsonSettings.Properties.Bottom.RetractHeight, JsonSettings.Properties.Exposure.RetractHeight);
}
return count;
}
public override void SaveAs(string filePath = null, OperationProgress progress = null)
{
if (RequireFullEncode)
+2 -2
View File
@@ -206,10 +206,10 @@ namespace UVtools.Core.FileFormats
{
set
{
UserSettings.MaxLayer = LayerCount - 1;
UserSettings.MaxLayer = LastLayerIndex;
ResinMetadataSettings.TotalLayersCount = LayerCount;
RebuildGCode();
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
}
}
+87 -2
View File
@@ -15,6 +15,7 @@ using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
using Stream = System.IO.Stream;
@@ -44,16 +45,44 @@ namespace UVtools.Core
/// </summary>
public Rectangle BoundingRectangle { get; internal set; } = Rectangle.Empty;
public bool IsBottomLayer => Index < ParentLayerManager.SlicerFile.BottomLayerCount;
public bool IsNormalLayer => !IsBottomLayer;
/// <summary>
/// Gets the layer index
/// </summary>
public uint Index { get; set; }
/// <summary>
/// Gets or sets the exposure time in seconds
/// Gets or sets the normal layer exposure time in seconds
/// </summary>
public float ExposureTime { get; set; }
/// <summary>
/// Gets or sets the layer off time in seconds
/// </summary>
public float LayerOffTime { get; set; }
/// <summary>
/// Gets or sets the lift height in mm
/// </summary>
public float LiftHeight { get; set; } = 5;
/// <summary>
/// Gets or sets the speed in mm/min
/// </summary>
public float LiftSpeed { get; set; } = 100;
/// <summary>
/// Gets the speed in mm/min for the retracts
/// </summary>
public float RetractSpeed { get; set; } = 100;
/// <summary>
/// Gets or sets the pwm value from 0 to 255
/// </summary>
public byte LightPWM { get; set; } = 255;
/// <summary>
/// Gets or sets the layer position on Z in mm
/// </summary>
@@ -245,7 +274,7 @@ namespace UVtools.Core
{
CvInvoke.FindNonZero(mat, nonZeroMat);
NonZeroPixelCount = (uint)nonZeroMat.Height;
BoundingRectangle = CvInvoke.BoundingRectangle(nonZeroMat);
BoundingRectangle = NonZeroPixelCount > 0 ? CvInvoke.BoundingRectangle(nonZeroMat) : Rectangle.Empty;
}
@@ -270,6 +299,62 @@ namespace UVtools.Core
return ParentLayerManager[Index + 1];
}
public bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value)
{
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.ExposureSeconds))
{
ExposureTime = (float)value;
return true;
}
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LayerOffTime))
{
LayerOffTime = (float)value;
return true;
}
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight))
{
LiftHeight = (float)value;
return true;
}
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed))
{
LiftSpeed = (float)value;
return true;
}
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed))
{
RetractSpeed = (float)value;
return true;
}
if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightPWM))
{
LightPWM = (byte)value;
return true;
}
return false;
}
public byte SetValuesFromPrintParametersModifiers(FileFormat.PrintParameterModifier[] modifiers)
{
if (modifiers is null) return 0;
byte changed = 0;
foreach (var modifier in modifiers)
{
if (!modifier.HasChanged) continue;
modifier.OldValue = modifier.NewValue;
SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
changed++;
}
return changed;
}
/// <summary>
/// Gets all islands start pixel location for this layer
/// https://www.geeksforgeeks.org/find-number-of-islands/
+13 -3
View File
@@ -165,15 +165,24 @@ namespace UVtools.Core
/// <summary>
/// Rebuild layer properties based on slice settings
/// </summary>
public void RebuildLayersProperties()
public void RebuildLayersProperties(bool recalculateZPos = true)
{
//var layerHeight = SlicerFile.LayerHeight;
for (uint layerIndex = 0; layerIndex < Count; layerIndex++)
{
var layer = this[layerIndex];
layer.Index = layerIndex;
layer.PositionZ = SlicerFile.GetHeightFromLayer(layerIndex);
layer.ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
layer.ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
layer.LiftHeight = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
layer.LiftSpeed = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
layer.RetractSpeed = SlicerFile.RetractSpeed;
layer.LightPWM = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
layer.LayerOffTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLayerOffTime, SlicerFile.LayerOffTime);
if (recalculateZPos)
{
layer.PositionZ = SlicerFile.GetHeightFromLayer(layerIndex);
}
}
}
@@ -212,6 +221,7 @@ namespace UVtools.Core
progress?.Reset(OperationProgress.StatusCalculatingBounds, Count);
for (int i = 1; i < Count; i++)
{
if(this[i].BoundingRectangle.IsEmpty) continue;
_boundingRectangle = Rectangle.Union(_boundingRectangle, this[i].BoundingRectangle);
if (ReferenceEquals(progress, null)) continue;
progress++;
+10 -5
View File
@@ -190,6 +190,8 @@ namespace UVtools.Core.Operations
{
if(!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
RaisePropertyChanged(nameof(LightOffDelay));
BottomRetractSpeed = _retractSpeed;
}
}
@@ -223,9 +225,9 @@ namespace UVtools.Core.Operations
}
}
public decimal LightOffDelay => Calculate(_liftHeight, _liftSpeed, _retractSpeed, _waitTime);
public decimal LightOffDelay => CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, _waitTime);
public decimal BottomLightOffDelay => Calculate(_bottomLiftHeight, _bottomLiftSpeed, _bottomRetractSpeed, _bottomWaitTime);
public decimal BottomLightOffDelay => CalculateSeconds(_bottomLiftHeight, _bottomLiftSpeed, _bottomRetractSpeed, _bottomWaitTime);
public LightOffDelayC()
{
@@ -243,7 +245,7 @@ namespace UVtools.Core.Operations
_bottomWaitTime = bottomWaitTime;
}
public static decimal Calculate(decimal liftHeight, decimal liftSpeed, decimal retract, decimal waitTime = 0)
public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retract, decimal waitTime = 0)
{
try
{
@@ -255,11 +257,11 @@ namespace UVtools.Core.Operations
}
}
public static float Calculate(float liftHeight, float liftSpeed, float retract, float waitTime = 0)
public static float CalculateSeconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0)
{
try
{
return (float) Math.Round(liftHeight / (liftSpeed / 60f) + liftHeight / (retract / 60f) + waitTime, 2);
return (float) Math.Round(liftHeight / (liftSpeed / 60f) + liftHeight / (retract / 60f) + extraWaitTime, 2);
}
catch (Exception)
{
@@ -267,6 +269,9 @@ namespace UVtools.Core.Operations
}
}
public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) =>
(uint) (CalculateSeconds(liftHeight, liftSpeed, retract, extraWaitTime) * 1000);
}
}
}
@@ -15,6 +15,8 @@ namespace UVtools.Core.Operations
{
public class OperationEditParameters : Operation
{
private bool _perLayerOverride;
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI { get; set; } = false;
@@ -22,7 +24,8 @@ namespace UVtools.Core.Operations
public override string Title => "Edit print parameters";
public override string Description =>
"Edits the available print parameters.";
"Edits the available print parameters.\n" +
"Note: Set global parameters will override all per layer settings when they are available.";
public override string ConfirmationText
{
@@ -34,7 +37,20 @@ namespace UVtools.Core.Operations
if(!modifier.HasChanged) continue;
sb.AppendLine($"{modifier.Name}: {modifier.OldValue}{modifier.ValueUnit} » {modifier.NewValue}{modifier.ValueUnit}");
}
return $"commit print parameter changes?\n{sb}";
var text = "commit print parameter changes";
if (_perLayerOverride)
{
if (LayerRangeCount == 1)
{
text += $" to layer {LayerIndexStart}";
}
else
{
text += $" from layer {LayerIndexStart} to {LayerIndexEnd}";
}
}
return $"{text}?\n{sb}";
}
}
@@ -58,6 +74,15 @@ namespace UVtools.Core.Operations
public FileFormat.PrintParameterModifier[] Modifiers { get; set; }
/// <summary>
/// Gets or sets if parameters are global or per layer inside a layer range
/// </summary>
public bool PerLayerOverride
{
get => _perLayerOverride;
set => RaiseAndSetIfChanged(ref _perLayerOverride, value);
}
public OperationEditParameters()
{
}
+1 -1
View File
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
<Version>1.0.0.2</Version>
<Version>1.1.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
+8 -2
View File
@@ -7,18 +7,24 @@
The Product@Id attribute (ProductCode Property) will be a random GUID for each build. This is to support "Major Upgrades" where each install
is a seamless uninstall/reinstall.
Version="$(var.MSIProductVersion)"
-->
<Product Id="*" Name="UVtools" Language="1033" Version="$(var.MSIProductVersion)" Manufacturer="PTRTECH" UpgradeCode="1ea6d212-15c0-425e-b2ec-4b6c60817552">
<Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine" />
<MediaTemplate EmbedCab="yes" />
<!-- Major Upgrade Rule to disallow downgrades -->
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MajorUpgrade
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
IgnoreRemoveFailure="no"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
Schedule="afterInstallInitialize" />
<!--Common Launch Condition-->
<!-- Examples at http://wixtoolset.org/documentation/manual/v3/customactions/wixnetfxextension.html -->
<PropertyRef Id="NETFRAMEWORK45" />
<Condition Message="[ProductName] requires .NET Framework 4.8.">Installed OR NETFRAMEWORK45</Condition>
<!-- Include User Interface Experience -->
<Icon Id="Icon.ico" SourceFile="..\UVtools.GUI\UVtools.ico" />
<Icon Id="Icon.ico" SourceFile="..\UVtools.WPF\UVtools.ico" />
<Property Id="ARPPRODUCTICON" Value="Icon.ico"></Property>
<UIRef Id="UI" />
<!-- Include Features and Directories Fragment -->
+1 -1
View File
@@ -14,7 +14,7 @@
<!-- If MSIProductVersion still not known, try to get it from TFBuild Environments (V.Next Builds)-->
<MSIProductVersion Condition=" '$(MSIProductVersion)' == '' ">$([System.Text.RegularExpressions.Regex]::Match($(BUILD_BUILDNUMBER), "\d+.\d+.\d+.\d+"))</MSIProductVersion>
<!-- If MSIProductVersion still not known, default to lowerbound 0.0.1 for developer builds.-->
<MSIProductVersion Condition=" '$(MSIProductVersion)' == '' ">1.0.0</MSIProductVersion>
<MSIProductVersion Condition=" '$(MSIProductVersion)' == '' ">1.1.0</MSIProductVersion>
<!-- The following allows one cert to be referenced from the certificate store for self-signing in localbuilds and another cert to be passed in during official builds. -->
<AppxCertificateThumbprint Condition=" '$(AppxCertificateThumbprint)' == '' ">
</AppxCertificateThumbprint>
@@ -119,6 +119,9 @@
<Component Id="owc7609DFEE03B95E830FD72B0699C9CF8C" Guid="c7b987b7-1a3c-0d2c-7831-ad6f4c3871e7">
<File Id="owf7609DFEE03B95E830FD72B0699C9CF8C" Source="$(var.SourceDir)\Microsoft.Win32.SystemEvents.dll" KeyPath="yes" />
</Component>
<Component Id="owcC64805E458FD0630011C0F519E9CA9E7" Guid="4b534498-a1a1-b38c-3d4b-d63b915c6d88">
<File Id="owfC64805E458FD0630011C0F519E9CA9E7" Source="$(var.SourceDir)\Microsoft.Windows.SDK.NET.dll" KeyPath="yes" />
</Component>
<Component Id="owc3FC8434C1A84605AEEBC6A100CA2F3E1" Guid="47d7e276-db6d-424e-d797-1a69e1e24b3b">
<File Id="owf3FC8434C1A84605AEEBC6A100CA2F3E1" Source="$(var.SourceDir)\Newtonsoft.Json.dll" KeyPath="yes" />
</Component>
+1
View File
@@ -15,6 +15,7 @@ namespace UVtools.WPF
public static class AppSettings
{
public static Version Version => Assembly.GetEntryAssembly().GetName().Version;
public static string VersionStr => Version.ToString(3);
// Supported ZoomLevels for Layer Preview.
// These settings eliminate very small zoom factors from the ImageBox default values,
// while ensuring that 4K/5K build plates can still easily fit on screen.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

@@ -4,48 +4,55 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolEditParametersControl">
<Grid
Name="grid"
RowDefinitions="Auto"
ColumnDefinitions="Auto,Auto,Auto,Auto,Auto"
VerticalAlignment="Center"
ShowGridLines="True"
<StackPanel Spacing="15">
<CheckBox
IsChecked="{Binding Operation.PerLayerOverride}"
IsVisible="{Binding SupportPerLayerSettings}"
Content="Change settings per a layer range"/>
<Grid
Name="grid"
RowDefinitions="Auto"
ColumnDefinitions="Auto,Auto,Auto,Auto,*"
VerticalAlignment="Center"
ShowGridLines="True"
>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Property"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Old value"/>
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="New value"/>
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Unit"/>
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Reset"/>
</Grid>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Property"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Old value"/>
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="New value"/>
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Unit"/>
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Padding="15"
Text="Reset"/>
</Grid>
</StackPanel>
</UserControl>
@@ -1,4 +1,7 @@
using System.Globalization;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
@@ -14,8 +17,10 @@ namespace UVtools.WPF.Controls.Tools
public class ToolEditParametersControl : ToolControl
{
public OperationEditParameters Operation { get; }
public bool SupportPerLayerSettings => App.SlicerFile.SupportPerLayerSettings;
public RowControl[] RowControls;
private Grid grid;
public sealed class RowControl
{
@@ -113,7 +118,23 @@ namespace UVtools.WPF.Controls.Tools
return;
}
Grid grid = this.FindControl<Grid>("grid");
grid = this.FindControl<Grid>("grid");
PopulateGrid();
Operation.PropertyChanged += OperationOnPropertyChanged;
}
public void PopulateGrid()
{
const byte cols = 5;
if (grid.Children.Count > cols)
{
grid.Children.RemoveRange(cols, grid.Children.Count - cols);
}
if (grid.RowDefinitions.Count > 1)
{
grid.RowDefinitions.RemoveRange(1, grid.RowDefinitions.Count-1);
}
int rowIndex = 1;
RowControls = new RowControl[Operation.Modifiers.Length];
@@ -122,7 +143,7 @@ namespace UVtools.WPF.Controls.Tools
{
grid.RowDefinitions.Add(new RowDefinition());
byte column = 0;
var rowControl = new RowControl(modifier);
grid.Children.Add(rowControl.Name);
grid.Children.Add(rowControl.OldValue);
@@ -166,6 +187,8 @@ namespace UVtools.WPF.Controls.Tools
{
case ToolWindow.Callbacks.Init:
ParentWindow.IsButton1Visible = true;
ParentWindow.SelectCurrentLayer();
ParentWindow.LayerRangeSync = true;
break;
case ToolWindow.Callbacks.Button1:
foreach (var rowControl in RowControls)
@@ -175,5 +198,32 @@ namespace UVtools.WPF.Controls.Tools
break;
}
}
private void OperationOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Operation.LayerIndexStart))
{
App.SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart);
PopulateGrid();
return;
}
if (e.PropertyName == nameof(Operation.PerLayerOverride))
{
if (Operation.PerLayerOverride)
{
Operation.Modifiers = App.SlicerFile.PrintParameterPerLayerModifiers;
App.SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart);
}
else
{
Operation.Modifiers = App.SlicerFile.PrintParameterModifiers;
App.SlicerFile.RefreshPrintParametersModifiersValues();
}
ParentWindow.LayerRangeVisible = Operation.PerLayerOverride;
PopulateGrid();
return;
}
}
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ namespace UVtools.WPF.Extensions
ContentMessage = message,
Icon = icon,
Style = style,
WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")),
//WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")),
WindowStartupLocation = location,
CanResize = false
});
+2
View File
@@ -28,6 +28,8 @@ namespace UVtools.WPF
SlicerFile.RebuildGCode();
RaisePropertyChanged(nameof(GCodeLines));
RaisePropertyChanged(nameof(GCodeStr));
CanSave = true;
}
public async void OnClickGCodeSaveFile()
+13 -3
View File
@@ -149,13 +149,23 @@ namespace UVtools.WPF
LayerImageBox.PointerPressed += LayerImageBoxOnPointerPressed;
LayerImageBox.DoubleTapped += LayerImageBoxOnDoubleTapped;
_issuesSliderCanvas.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
LayerSlider.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
//this.FindControl<Grid>("LayerNavigationSliderGrid").PointerWheelChanged += LayerSliderOnPointerWheelChanged;
_layerNavigationTooltipTimer.Elapsed += (sender, args) =>
{
Dispatcher.UIThread.InvokeAsync(() => RaisePropertyChanged(nameof(LayerNavigationTooltipMargin)));
};
}
private void LayerSliderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
if (e.Delta.Y > 0)
ActualLayer++;
else if (e.Delta.Y < 0 && _actualLayer > 0)
ActualLayer--;
}
public bool ShowLayerImageRotated
@@ -268,7 +278,7 @@ namespace UVtools.WPF
public string MaximumLayerString => SlicerFile is null ? "???" : $"{SlicerFile.TotalHeight}mm\n{SlicerFile.LayerCount - 1}";
public string ActualLayerTooltip => SlicerFile is null ? "???" : $"{SlicerFile.GetHeightFromLayer(ActualLayer):0.00}mm\n{ActualLayer}\n{(ActualLayer + 1) * 100 / (SlicerFile.LayerCount)}%";
public uint SliderMaximumValue => SlicerFile?.LayerCount - 1 ?? 0;
public uint SliderMaximumValue => SlicerFile?.LastLayerIndex ?? 0;
public bool CanGoUp => _actualLayer < SliderMaximumValue;
public bool CanGoDown => _actualLayer > 0;
@@ -606,7 +616,7 @@ namespace UVtools.WPF
Settings.LayerPreview.VolumeBoundsOutlineThickness);
}
if (_showLayerOutlineLayerBoundary)
if (_showLayerOutlineLayerBoundary && !SlicerFile[_actualLayer].BoundingRectangle.IsEmpty)
{
CvInvoke.Rectangle(LayerCache.ImageBgr, SlicerFile[_actualLayer].BoundingRectangle,
new MCvScalar(Settings.LayerPreview.LayerBoundsOutlineColor.B,
+15 -5
View File
@@ -172,6 +172,14 @@
</MenuItem.Icon>
</MenuItem>
<MenuItem
Header="_Open settings folder"
Command="{Binding MenuHelpOpenSettingsFolderClicked}">
<MenuItem.Icon>
<Image Source="\Assets\Icons\open-16x16.png"/>
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem
@@ -180,8 +188,7 @@
<MenuItem.Icon>
<Image Source="\Assets\Icons\CNCMachine-16x16.png"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</MenuItem>
<MenuItem
@@ -1281,10 +1288,12 @@
<Image Width="16" Height="16" Source="/Assets/Icons/arrow-up-16x16.png"/>
</RepeatButton>
<Grid Grid.Row="2" ColumnDefinitions="*,20,Auto">
<Grid
Name="LayerNavigationSliderGrid"
Grid.Row="2" ColumnDefinitions="*,20,Auto">
<Panel
Grid.Column="0"
Name="Layer.Navigation.Tooltip.Panel"
Name="LayerNavigationTooltipPanel"
Margin="{Binding LayerNavigationTooltipMargin}"
HorizontalAlignment="Left"
>
@@ -1307,7 +1316,8 @@
Grid.Column="1"
Margin="0,15"
Background="WhiteSmoke"
Name="Layer.Navigation.IssuesCanvas" Width="20"/>
Name="Layer.Navigation.IssuesCanvas" Width="20"
/>
<uc:SliderEx
Grid.Column="2"
+8 -3
View File
@@ -665,6 +665,11 @@ namespace UVtools.WPF
await new BenchmarkWindow().ShowDialog(this);
}
public void MenuHelpOpenSettingsFolderClicked()
{
App.StartProcess(UserSettings.SettingsFolder);
}
public async void MenuHelpInstallProfilesClicked()
{
var PEFolder = App.GetPrusaSlicerDirectory();
@@ -690,8 +695,8 @@ namespace UVtools.WPF
private void UpdateTitle()
{
Title = (SlicerFile is null
? $"{About.Software} Version: {AppSettings.Version}"
: $"{About.Software} File: {Path.GetFileName(SlicerFile.FileFullPath)} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) Version: {AppSettings.Version}")
? $"{About.Software} Version: {AppSettings.VersionStr}"
: $"{About.Software} File: {Path.GetFileName(SlicerFile.FileFullPath)} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) Version: {AppSettings.VersionStr}")
;
#if DEBUG
@@ -1103,7 +1108,7 @@ namespace UVtools.WPF
{
SlicerFile.SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
}*/
SlicerFile.SetValuesFromPrintParametersModifiers();
SlicerFile.EditPrintParameters(operation);
RefreshProperties();
ResetDataContext();
+1 -1
View File
@@ -48,7 +48,7 @@ namespace UVtools.WPF.Structures
searchFor.Length;
var endIndex = htmlCode.IndexOf("\"", startIndex, StringComparison.InvariantCultureIgnoreCase);
var version = htmlCode.Substring(startIndex, endIndex - startIndex);
if (string.Compare(version, $"v{AppSettings.AssemblyVersion}", StringComparison.OrdinalIgnoreCase) > 0)
if (string.Compare(version, $"v{AppSettings.VersionStr}", StringComparison.OrdinalIgnoreCase) > 0)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
+7 -7
View File
@@ -13,7 +13,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<Nullable>enable</Nullable>
<Version>1.0.0.2</Version>
<Version>1.1.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -24,16 +24,16 @@
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.999-cibuild0011401-beta" />
<PackageReference Include="Avalonia" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.999-cibuild0011401-beta" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.999-cibuild0011401-beta" />
<PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview3" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview6" />
<PackageReference Include="Emgu.CV.runtime.raspbian" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.ubuntu" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4099" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.2-night" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.0-preview3" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.0-preview6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UVtools.Core\UVtools.Core.csproj" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 126 KiB

+2 -2
View File
@@ -12,13 +12,13 @@
<StackPanel Orientation="Vertical">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<Image Source="/Assets/Icons/UVtools.ico" Width="256"/>
<Image Margin="20,0,0,0" Source="/Assets/Icons/UVtools_alt.ico" Width="256"/>
<Grid
RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,*"
Margin="20"
>
<TextBlock Grid.Row="0" Text="{Binding Software}"/>
<TextBlock Grid.Row="0" Text="{Binding Software}" FontWeight="Bold"/>
<TextBlock Grid.Row="2" Text="{Binding Version}"/>
<TextBlock Grid.Row="4" Text="{Binding Copyright}"/>
<TextBlock Grid.Row="6" Text="{Binding Company}"/>
+1 -1
View File
@@ -7,7 +7,7 @@ namespace UVtools.WPF.Windows
public class AboutWindow : WindowEx
{
public string Software => About.Software;
public string Version => $"Version: {AppSettings.Version}";
public string Version => $"Version: {AppSettings.VersionStr}";
public string Copyright => AppSettings.AssemblyCopyright;
public string Company => AppSettings.AssemblyCompany;
public string Description => AppSettings.AssemblyDescription;
+1 -1
View File
@@ -43,7 +43,7 @@ namespace UVtools.WPF.Windows
public SettingsWindow()
{
Title += $" [v{AppSettings.Version}]";
Title += $" [v{AppSettings.VersionStr}]";
SettingsBackup = UserSettings.Instance.Clone();
var fileFormats = new List<string>
+19 -1
View File
@@ -37,7 +37,23 @@
IsVisible="{Binding LayerRangeVisible}">
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Layer range selector"/>
<Grid
ColumnDefinitions="Auto,*" Background="LightBlue">
<TextBlock
Padding="10" FontWeight="Bold"
VerticalAlignment="Center"
Text="Layer range selector"/>
<CheckBox
Grid.Column="1"
Padding="10"
FontWeight="Bold"
VerticalAlignment="Center"
HorizontalAlignment="Right"
IsChecked="{Binding LayerRangeSync}"
ToolTip.Tip="Synchronize and lock the layer range for single layer navigation"
Content="Synchronize"/>
</Grid>
<Grid
RowDefinitions="Auto,5,Auto"
@@ -65,6 +81,7 @@
Grid.Column="2"
VerticalAlignment="Center"
Text="To:"
IsEnabled="{Binding !LayerRangeSync}"
Margin="20,0,10,0"
/>
@@ -73,6 +90,7 @@
Grid.Column="3"
VerticalAlignment="Center"
Maximum="{Binding MaximumLayerIndex}"
IsEnabled="{Binding !LayerRangeSync}"
Value="{Binding LayerIndexEnd}"
/>
+21
View File
@@ -25,6 +25,7 @@ namespace UVtools.WPF.Windows
private string _description;
private double _descriptionMaxWidth;
private bool _layerRangeVisible = true;
private bool _layerRangeSync;
private uint _layerIndexStart;
private uint _layerIndexEnd;
private bool _isROIVisible;
@@ -72,6 +73,19 @@ namespace UVtools.WPF.Windows
set => RaiseAndSetIfChanged(ref _layerRangeVisible, value);
}
public bool LayerRangeSync
{
get => _layerRangeSync;
set
{
if(!RaiseAndSetIfChanged(ref _layerRangeSync, value)) return;
if (_layerRangeSync)
{
LayerIndexEnd = _layerIndexStart;
}
}
}
public uint LayerIndexStart
{
get => _layerIndexStart;
@@ -83,6 +97,13 @@ namespace UVtools.WPF.Windows
if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
RaisePropertyChanged(nameof(LayerStartMM));
RaisePropertyChanged(nameof(LayerRangeCountStr));
if (_layerRangeSync)
{
LayerIndexEnd = _layerIndexStart;
}
App.MainWindow.ActualLayer = _layerIndexStart;
}
}