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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@@ -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 |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
@@ -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 |
@@ -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);
|
||||
|
||||
@@ -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 printer’s 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -716,7 +716,6 @@ namespace UVtools.Core.FileFormats
|
||||
LayerManager = LayerManager,
|
||||
HeaderSettings =
|
||||
{
|
||||
Version = 2,
|
||||
BedSizeX = PrinterSettings.DisplayWidth,
|
||||
BedSizeY = PrinterSettings.DisplayHeight,
|
||||
BedSizeZ = PrinterSettings.MaxPrintHeight,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -206,10 +206,10 @@ namespace UVtools.Core.FileFormats
|
||||
{
|
||||
set
|
||||
{
|
||||
UserSettings.MaxLayer = LayerCount - 1;
|
||||
UserSettings.MaxLayer = LastLayerIndex;
|
||||
ResinMetadataSettings.TotalLayersCount = LayerCount;
|
||||
RebuildGCode();
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(NormalLayerCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 126 KiB |
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace UVtools.WPF
|
||||
SlicerFile.RebuildGCode();
|
||||
RaisePropertyChanged(nameof(GCodeLines));
|
||||
RaisePropertyChanged(nameof(GCodeStr));
|
||||
|
||||
CanSave = true;
|
||||
}
|
||||
|
||||
public async void OnClickGCodeSaveFile()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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(() =>
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 126 KiB |
@@ -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}"/>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}"
|
||||
/>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||