pcut_uwp/PCUT/PCUT/Pages/DesignCenter/SvgNestPage.xaml.cs

426 lines
16 KiB
C#

using DeepNestLib;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using PCUT.Extensions;
using PCUT.DeepNestApi;
using PCUT.ViewModels;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using System.ComponentModel;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace PCUT.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SvgNestPage : Page
{
private SvgData _svgData => SvgData.Instance;
private SvgNestSettings _settings => SvgNestSettings.Instance;
private DesignCenterViewModel _designCenterModel;
public ImageViewModel PreviewModel { get; set; }
public NestingProgressModel ProgressModel { get; set; }
private ConcurrentQueue<(XDocument Svg, double Width, double Height, double MaterialUtilization)> NestedResult { get; set; }
= new ConcurrentQueue<(XDocument, double, double, double)>();
public SvgNestPage()
{
this.InitializeComponent();
PreviewModel = new ImageViewModel();
ProgressModel = new NestingProgressModel();
DeepNestApi.Background.UseParallel = true;
_ = _settings;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (_svgData.Data == null)
return;
_designCenterModel = e.Parameter as DesignCenterViewModel;
_designCenterModel.PropertyChanged += UpdateSizeChanged;
DeepNestApi.Background.displayProgress = (progress) => DispatchAsync(DisplayNestingProgress, progress);
_ = Initialize();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
//if (e.SourcePageType != typeof(EditPage))
// _svgData.SetData(null);
StopNesting();
_designCenterModel.PropertyChanged -= UpdateSizeChanged;
DeepNestApi.Background.displayProgress = null;
}
private static readonly object _boundingSizeLock = new object();
private void UpdateSizeChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(DesignCenterViewModel.Width))
{
var model = sender as DesignCenterViewModel;
StopNesting();
_svgData.SetResult(null);
_designCenterModel.MinSize = 0;
_ = HandleSizeChangedAsync(width: model.Width);
}
else if (e.PropertyName == nameof(DesignCenterViewModel.Height))
{
var model = sender as DesignCenterViewModel;
StopNesting();
_svgData.SetResult(null);
_designCenterModel.MinSize = 0;
_ = HandleSizeChangedAsync(height: model.Height);
}
}
private async Task HandleSizeChangedAsync(double? width = null, double? height = null)
{
if (Monitor.TryEnter(_boundingSizeLock, 100))
{
try
{
var bound = _svgData.Data.GetDescendantById("PCUT_BOUNDING");
if (width != null)
{
bound.SetAttributeValue("width", width.Value);
}
if (height != null)
{
bound.SetAttributeValue("height", height.Value);
}
PreviewModel.ShowDefault();
_currentResult = null;
_svgData.CleanUpContext();
await PreviewModel.Source.LoadSvgAsync(_svgData.Data);
}
finally
{
Monitor.Exit(_boundingSizeLock);
}
}
}
private void MainScroll_Loading(FrameworkElement sender, object args)
{
var borderRatio = PreviewColumnDefinition.ActualWidth / PreviewRowDefinition.ActualHeight;
var ratio = _svgData.OriginWidth.Value / _svgData.OriginHeight.Value;
if (ratio < borderRatio)
((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewRowDefinition, ratio);
else
((ComponentStyle)Resources["previewStyle"]).SetSize(PreviewColumnDefinition, ratio);
}
private void MainPreview_ImageOpened(object sender, RoutedEventArgs e)
{
HideProgressRing();
}
private void MainScroll_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var (offsetX, offsetY) = e.Delta.CalculateTranslation(e.Velocities);
var scrollViewer = sender as ScrollViewer;
_ = scrollViewer.ChangeView(scrollViewer.HorizontalOffset - offsetX, scrollViewer.VerticalOffset - offsetY, scrollViewer.ZoomFactor);
}
private void MainPreviewRefresh_Click(object sender, RoutedEventArgs e)
{
MainScroll.ChangeView(0, 0, 1);
}
private void MainPreviewDefault_Click(object sender, RoutedEventArgs e)
{
PreviewModel.ShowDefault();
MainScroll.ChangeView(0, 0, 1);
_designCenterModel.UseOrigin = true;
}
private void NestStart_Click(object sender, RoutedEventArgs e)
{
_ = StartAsync(StartNesting);
}
private void NestStop_Click(object sender, RoutedEventArgs e)
{
StopNesting();
}
private void NestResult_Click(object sender, RoutedEventArgs e)
{
ShowNestedResult();
}
private async void NestSettings_Click(object sender, RoutedEventArgs e)
{
var nestSettingDialog = new SvgNestSettingDialog();
await nestSettingDialog.ShowAsync();
}
// private nesting logic
//private double _maxOfMinSize;
//private double _totalArea;
private async Task Initialize()
{
await StartAsync(LoadDetail);
_svgData.ComputeDetailBounds();
PreviewModel.SetSource(_svgData.Data);
}
private void LoadDetail()
{
if (_svgData.NestingContext == null)
{
if (_svgData.ComponentDetails == null || !_svgData.ComponentDetails.Any())
{
var doc = new XDocument(_svgData.Data);
doc.SetSize(_svgData.OriginWidth.Value, _svgData.OriginHeight.Value);
doc.RemoveHidden();
var (details, scale) = SvgParser.LoadSvg(doc, true);
foreach (var detail in details)
_svgData.AddDetail(detail);
_svgData.DetailScaleFactor = scale;
}
}
if (_svgData.NestedData != null)
{
DispatchAsync(UpdateCurrentSource);
}
}
private List<NFP> _sheets => _svgData.NestingContext.Sheets;
private List<NFP> _polygons => _svgData.NestingContext.Polygons;
private void StartNesting()
{
DispatchAsync(ShowProgressRing);
var requireStartNest = _svgData.NestingContext == null;
if (requireStartNest)
{
var totalArea = 0d;
var maxOfMinSize = 0d;
foreach (var (cWidth, cHeight) in _svgData.DetailBounds)
{
var minSize = Math.Ceiling(Math.Min(cWidth, cHeight));
if (maxOfMinSize < minSize)
maxOfMinSize = minSize;
totalArea += cWidth * cHeight;
}
var width = _designCenterModel.Width;
var height = _designCenterModel.Height;
// check if width/height (which one is smaller) can accomodate the largest component, based on its smaller dimension.
if (width < height)
{
if (width < maxOfMinSize)
width = maxOfMinSize;
if (totalArea > width * height)
height = Math.Ceiling(totalArea / width);
}
else
{
if (height < maxOfMinSize)
height = maxOfMinSize;
if (totalArea > width * height)
width = Math.Ceiling(totalArea / height);
}
_svgData.NewSheetInfo(width, height);
_svgData.NestingContext = new NestingContext();
int src = 0;
if (_svgData.SheetInfo != null)
{
src = _svgData.NestingContext.GetNextSheetSource();
for (int i = 0; i < _svgData.SheetInfo.Quantity; i++)
{
var ns = DeepNestApi.Background.clone(_svgData.SheetInfo.Nfp);
Sheet sheet = new Sheet();
sheet.Points = ns.Points;
sheet.children = ns.children;
_sheets.Add(sheet);
sheet.Width = sheet.WidthCalculated;
sheet.Height = sheet.HeightCalculated;
sheet.source = src;
}
}
_svgData.NestingContext.ReorderSheets();
_svgData.DxfCache.Clear();
src = 0;
if (_svgData.ComponentDetails != null && _svgData.ComponentDetails.Any())
{
foreach (var detail in _svgData.ComponentDetails)
{
var t1 = detail.Outers.Where(z => z.Tag is object[]).Select(z => z.Tag as object[]).SelectMany(z => z).ToArray();
foreach (var outter in detail.Outers)
{
t1 = t1.Union(outter.Childrens.Where(z => z.Tag is object[]).Select(z => z.Tag as object[]).SelectMany(z => z).ToArray()).ToArray();
}
_svgData.DxfCache.Add(detail.Name, t1);
}
foreach (var detail in _svgData.ComponentDetails)
{
_svgData.NestingContext.ImportFromRawDetail(detail, src);
src++;
}
}
}
if (_sheets.Count == 0 || _polygons.Count == 0)
{
DispatchAsync(HideProgressRing);
DispatchAsync(StopNesting);
return;
}
//menu.SetTab(menu.NestTab);
_svgData.NestingContext.ReorderSheets();
RunDeepnest(_settings.MaxNumberOfRun, requireStartNest);
}
private ImageSource _currentResult;
private readonly ConcurrentQueue<object> _runQueue = new ConcurrentQueue<object>();
private static readonly object _runCommand = new object();
private void RunDeepnest(int maxNumberOfRun, bool requiredStartNest)
{
while (maxNumberOfRun > 0)
{
_runQueue.Enqueue(_runCommand);
maxNumberOfRun--;
}
DispatchAsync(HideProgressRing);
DispatchAsync(() => ProgressModel.Running = true);
if (requiredStartNest)
_svgData.NestingContext.StartNest();
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(_settings.MaxRuntimeInMinute));
while (!cancellationTokenSource.Token.IsCancellationRequested)
{
if (!_runQueue.TryDequeue(out _))
break;
double materialUtilization = _svgData.MaterialUtilization;
DispatchAsync(DisplayNestingProgress, 0.0d);
_svgData.NestingContext.NestIterate();
DispatchAsync(DisplayNestingProgress, 1.0d);
if (_polygons.Count(z => z.fitted) == _svgData.DetailCount && _svgData.NestingContext.MaterialUtilization <= materialUtilization)
{
materialUtilization = _svgData.NestingContext.MaterialUtilization;
var (doc, width, height) = Export();
NestedResult.Enqueue((doc, width, height, materialUtilization));
DispatchAsync(UpdatePreview);
}
}
if (ProgressModel?.Running ?? false)
DispatchAsync(StopNesting);
}
private void DisplayNestingProgress(double progress)
{
if (ProgressModel != null)
ProgressModel.Progress = Math.Floor(progress * 100);
}
private void UpdatePreview()
{
if (NestedResult.TryDequeue(out var result))
{
_svgData.SetResult(result.Svg, result.Width, result.Height, result.MaterialUtilization);
if (ProgressModel?.Running ?? false)
{
var minSize = Math.Min(_svgData.SheetInfo.Width, _svgData.SheetInfo.Height);
if (minSize > Math.Min(_designCenterModel.Width, _designCenterModel.Height))
_designCenterModel.MinSize = Math.Ceiling(minSize);
else
_designCenterModel.MinSize = 0;
var switchToResult = _currentResult == null && PreviewModel.DefaultMode;
UpdateCurrentSource();
if (switchToResult)
ShowNestedResult();
}
}
}
private void UpdateCurrentSource()
{
if (_currentResult == null)
_currentResult = new SvgImageSource();
_ = _currentResult.LoadSvgAsync(_svgData.NestedData);
}
private void ShowNestedResult()
{
PreviewModel.SetSource(_currentResult);
_designCenterModel.UseOrigin = false;
}
private void StopNesting()
{
if (ProgressModel != null)
ProgressModel.Running = false;
_runQueue.Clear();
}
private void ShowProgressRing()
{
LoadingRing.IsActive = true;
LoadingRing.Visibility = Visibility.Visible;
}
private void HideProgressRing()
{
LoadingRing.IsActive = false;
LoadingRing.Visibility = Visibility.Collapsed;
}
private (XDocument NestedDoc, double Width, double Height) Export()
{
var polygons = _polygons.Where(x => x.fitted).ToArray();
foreach (var item in polygons)
{
item.Tag = _svgData.DxfCache[item.Name];
}
var (nestedDoc, nestedWidth, nestedHeight) = SvgParser.Export(polygons);
var width = _svgData.SheetInfo.Width;
var height = _svgData.SheetInfo.Height;
var viewBox = _svgData.OriginViewBox.GetViewBoxData();
nestedDoc.Root.SetAttributeValue("viewBox", $"0 0 {nestedWidth} {nestedHeight}");
nestedDoc.Root.Add(XElement.Parse($"<rect id=\"PCUT_BOUNDING\" x=\"0\" y=\"0\" width=\"{_designCenterModel.Width}\" height=\"{_designCenterModel.Height}\" fill=\"none\" stroke=\"blue\" stroke-dasharray=\"{_designCenterModel.Width / 200}\" stroke-width=\"{_designCenterModel.Height / 2000}\" />"));
nestedDoc.Root.Add(XElement.Parse($"<rect id=\"PCUT_BOUNDING_NESTED\" x=\"0\" y=\"0\" width=\"{nestedWidth}\" height=\"{nestedHeight}\" fill=\"none\" stroke=\"blue\" stroke-dasharray=\"{width / 200}\" stroke-width=\"{width / 2000}\" />"));
return (nestedDoc, nestedWidth, nestedHeight);
}
private Task StartAsync(Action action)
{
return Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);
}
private async void DispatchAsync(Action action)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action.Invoke());
}
private async void DispatchAsync<T>(Action<T> action, T param)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action.Invoke(param));
}
}
}