commit f2595d7cbaafee4db17bf74e226e20c35326faf4 Author: aj Date: Mon May 16 22:09:19 2016 -0400 First commit. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..928a5ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,200 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# NSIS installer directory +Installer/* +!*.nsi + +# Visual Studo 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.[Cc]ache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt diff --git a/External/Ionic.Zip.dll b/External/Ionic.Zip.dll new file mode 100644 index 0000000..195af81 Binary files /dev/null and b/External/Ionic.Zip.dll differ diff --git a/External/netDxf.dll b/External/netDxf.dll new file mode 100644 index 0000000..66b2c29 Binary files /dev/null and b/External/netDxf.dll differ diff --git a/Installer/script.nsi b/Installer/script.nsi new file mode 100644 index 0000000..cfc5331 --- /dev/null +++ b/Installer/script.nsi @@ -0,0 +1,138 @@ +!include "MUI2.nsh" + +Name "OpenNest" + +OutFile "opennest-setup.exe" + +InstallDir "$PROGRAMFILES\OpenNest" + +RequestExecutionLevel admin + +!define DOT_MAJOR "4" +!define DOT_MINOR "0" + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "../license.txt" +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" + +UninstPage uninstConfirm +UninstPage instfiles + +Section "OpenNest (required)" + Call IsDotNETInstalled + SectionIn RO +#!/usr/bin/env + ; Set output path to the installation directory. + SetOutPath $INSTDIR + + File "OpenNest.exe" + File "OpenNest.Core.dll" + File "OpenNest.Engine.dll" + File "Ionic.Zip.dll" + File "netDxf.dll" + + ; Write the installation path into the registry + WriteRegStr HKLM SOFTWARE\OpenNest "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenNest" "DisplayName" "OpenNest" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenNest" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenNest" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenNest" "NoRepair" 1 + WriteUninstaller "uninstall.exe" +SectionEnd + +Section "Start Menu Shortcuts" + CreateDirectory "$SMPROGRAMS\OpenNest" + CreateShortCut "$SMPROGRAMS\OpenNest\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 + CreateShortCut "$SMPROGRAMS\OpenNest\OpenNest.lnk" "$INSTDIR\OpenNest.exe" "" "$INSTDIR\OpenNest.exe" 0 +SectionEnd + +Section "Uninstall" + Delete "$INSTDIR\uninstall.exe" + Delete "$INSTDIR\OpenNest.exe" + Delete "$INSTDIR\OpenNest.Core.dll" + Delete "$INSTDIR\OpenNest.Engine.dll" + Delete "$INSTDIR\Ionic.Zip.dll" + Delete "$INSTDIR\netDxf.dll" + + Delete "$SMPROGRAMS\OpenNest\Uninstall.lnk" + Delete "$SMPROGRAMS\OpenNest\OpenNest.lnk" + Delete "$SMPROGRAMS\OpenNest" +SectionEnd + +; Usage +; Define in your script two constants: +; DOT_MAJOR "(Major framework version)" +; DOT_MINOR "{Minor framework version)" +; +; Call IsDotNetInstalled +; This function will abort the installation if the required version +; or higher version of the .NET Framework is not installed. Place it in +; either your .onInit function or your first install section before +; other code. +Function IsDotNetInstalled + + StrCpy $0 "0" + StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ;registry entry to look in. + StrCpy $2 0 + + StartEnum: + ;Enumerate the versions installed. + EnumRegKey $3 HKLM "$1\policy" $2 + + ;If we don't find any versions installed, it's not here. + StrCmp $3 "" noDotNet notEmpty + + ;We found something. + notEmpty: + ;Find out if the RegKey starts with 'v'. + ;If it doesn't, goto the next key. + StrCpy $4 $3 1 0 + StrCmp $4 "v" +1 goNext + StrCpy $4 $3 1 1 + + ;It starts with 'v'. Now check to see how the installed major version + ;relates to our required major version. + ;If it's equal check the minor version, if it's greater, + ;we found a good RegKey. + IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg + ;Check the minor version. If it's equal or greater to our requested + ;version then we're good. + StrCpy $4 $3 1 3 + IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext yesDotNetReg + + goNext: + ;Go to the next RegKey. + IntOp $2 $2 + 1 + goto StartEnum + + yesDotNetReg: + ;Now that we've found a good RegKey, let's make sure it's actually + ;installed by getting the install path and checking to see if the + ;mscorlib.dll exists. + EnumRegValue $2 HKLM "$1\policy\$3" 0 + ;$2 should equal whatever comes after the major and minor versions + ;(ie, v1.1.4322) + StrCmp $2 "" noDotNet + ReadRegStr $4 HKLM $1 "InstallRoot" + ;Hopefully the install root isn't empty. + StrCmp $4 "" noDotNet + ;build the actuall directory path to mscorlib.dll. + StrCpy $4 "$4$3.$2\mscorlib.dll" + IfFileExists $4 yesDotNet noDotNet + + noDotNet: + ;Nope, something went wrong along the way. Looks like the + ;proper .NET Framework isn't installed. + MessageBox MB_OK "You must have v${DOT_MAJOR}.${DOT_MINOR} or greater of the .NET Framework installed. Aborting!" + Abort + + yesDotNet: + ;Everything checks out. Go on with the rest of the installation. + +FunctionEnd \ No newline at end of file diff --git a/Source/OpenNest.Core/Align.cs b/Source/OpenNest.Core/Align.cs new file mode 100644 index 0000000..3de4469 --- /dev/null +++ b/Source/OpenNest.Core/Align.cs @@ -0,0 +1,186 @@ +using System.Collections.Generic; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class Align + { + public static void Vertically(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(fixedEntity.BoundingBox.Center.X - movableEntity.BoundingBox.Center.X, 0); + } + + public static void Vertically(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Vertically(fixedEntity, entity)); + } + + public static void Vertically(Part fixedPart, Part movablePart) + { + movablePart.Offset(fixedPart.BoundingBox.Center.X - movablePart.BoundingBox.Center.X, 0); + } + + public static void Vertically(Part fixedPart, List parts) + { + parts.ForEach(part => Vertically(fixedPart, part)); + } + + public static void Horizontally(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(0, fixedEntity.BoundingBox.Center.Y - movableEntity.BoundingBox.Center.Y); + } + + public static void Horizontally(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Horizontally(fixedEntity, entity)); + } + + public static void Horizontally(Part fixedPart, Part movablePart) + { + movablePart.Offset(0, fixedPart.BoundingBox.Center.Y - movablePart.BoundingBox.Center.Y); + } + + public static void Horizontally(Part fixedPart, List parts) + { + parts.ForEach(part => Horizontally(fixedPart, part)); + } + + public static void Left(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(fixedEntity.BoundingBox.Left - movableEntity.BoundingBox.Left, 0); + } + + public static void Left(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Left(fixedEntity, entity)); + } + + public static void Left(Part fixedPart, Part movablePart) + { + movablePart.Offset(fixedPart.BoundingBox.Left - movablePart.BoundingBox.Left, 0); + } + + public static void Left(Part fixedPart, List parts) + { + parts.ForEach(part => Left(fixedPart, part)); + } + + public static void Right(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(fixedEntity.BoundingBox.Right - movableEntity.BoundingBox.Right, 0); + } + + public static void Right(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Right(fixedEntity, entity)); + } + + public static void Right(Part fixedPart, Part movablePart) + { + movablePart.Offset(fixedPart.BoundingBox.Right - movablePart.BoundingBox.Right, 0); + } + + public static void Right(Part fixedPart, List parts) + { + parts.ForEach(part => Right(fixedPart, part)); + } + + public static void Top(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(0, fixedEntity.BoundingBox.Top - movableEntity.BoundingBox.Top); + } + + public static void Top(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Top(fixedEntity, entity)); + } + + public static void Top(Part fixedPart, Part movablePart) + { + movablePart.Offset(0, fixedPart.BoundingBox.Top - movablePart.BoundingBox.Top); + } + + public static void Top(Part fixedPart, List parts) + { + parts.ForEach(part => Top(fixedPart, part)); + } + + public static void Bottom(Entity fixedEntity, Entity movableEntity) + { + movableEntity.Offset(0, fixedEntity.BoundingBox.Bottom - movableEntity.BoundingBox.Bottom); + } + + public static void Bottom(Entity fixedEntity, List entities) + { + entities.ForEach(entity => Bottom(fixedEntity, entity)); + } + + public static void Bottom(Part fixedPart, Part movablePart) + { + movablePart.Offset(0, fixedPart.BoundingBox.Bottom - movablePart.BoundingBox.Bottom); + } + + public static void Bottom(Part fixedPart, List parts) + { + parts.ForEach(part => Bottom(fixedPart, part)); + } + + public static void EvenlyDistributeHorizontally(List parts) + { + if (parts.Count < 3) + return; + + var list = new List(parts); + list.Sort((p1, p2) => p1.BoundingBox.Center.X.CompareTo(p2.BoundingBox.Center.X)); + + var lastIndex = list.Count - 1; + + var first = list[0]; + var last = list[lastIndex]; + + var start = first.BoundingBox.Center.X; + var end = last.BoundingBox.Center.X; + var diff = end - start; + + var spacing = diff / lastIndex; + + for (int i = 1; i < lastIndex; ++i) + { + var part = list[i]; + var newX = start + i * spacing; + var curX = part.BoundingBox.Center.X; + + part.Offset(newX - curX, 0); + } + } + + public static void EvenlyDistributeVertically(List parts) + { + if (parts.Count < 3) + return; + + var list = new List(parts); + list.Sort((p1, p2) => p1.BoundingBox.Center.Y.CompareTo(p2.BoundingBox.Center.Y)); + + var lastIndex = list.Count - 1; + + var first = list[0]; + var last = list[lastIndex]; + + var start = first.BoundingBox.Center.Y; + var end = last.BoundingBox.Center.Y; + var diff = end - start; + + var spacing = diff / lastIndex; + + for (int i = 1; i < lastIndex; ++i) + { + var part = list[i]; + var newX = start + i * spacing; + var curX = part.BoundingBox.Center.Y; + + part.Offset(0, newX - curX); + } + } + } +} diff --git a/Source/OpenNest.Core/AlignType.cs b/Source/OpenNest.Core/AlignType.cs new file mode 100644 index 0000000..c56bfd1 --- /dev/null +++ b/Source/OpenNest.Core/AlignType.cs @@ -0,0 +1,15 @@ + +namespace OpenNest +{ + public enum AlignType + { + Left, + Right, + Top, + Bottom, + Horizontally, + Vertically, + EvenlySpaceHorizontally, + EvenlySpaceVertically + } +} diff --git a/Source/OpenNest.Core/Angle.cs b/Source/OpenNest.Core/Angle.cs new file mode 100644 index 0000000..fd70723 --- /dev/null +++ b/Source/OpenNest.Core/Angle.cs @@ -0,0 +1,125 @@ +using System; + +namespace OpenNest +{ + public static class Angle + { + /// + /// Number of radians equal to 1 degree. + /// + public const double RadPerDeg = Math.PI / 180.0; + + /// + /// Number of degrees equal to 1 radian. + /// + public const double DegPerRad = 180.0 / Math.PI; + + /// + /// Half of PI. + /// + public const double HalfPI = Math.PI * 0.5; + + /// + /// 2 x PI + /// + public const double TwoPI = Math.PI * 2.0; + + /// + /// Converts radians to degrees. + /// + /// + /// + public static double ToDegrees(double radians) + { + return DegPerRad * radians; + } + + /// + /// Converts degrees to radians. + /// + /// + /// + public static double ToRadians(double degrees) + { + return RadPerDeg * degrees; + } + + /// + /// Normalizes an angle. + /// The normalized angle will be in the range from 0 to TwoPI, + /// where TwoPI itself is not included. + /// + /// + /// + public static double NormalizeRad(double angle) + { + double r = angle % Angle.TwoPI; + return r < 0 ? Angle.TwoPI + r : r; + } + + /// + /// Normalizes an angle. + /// The normalized angle will be in the range from 0 to 360, + /// where 360 itself is not included. + /// + /// + /// + public static double NormalizeDeg(double angle) + { + double r = angle % 360.0; + return r < 0 ? 360.0 + r : r; + } + + /// + /// Whether the given angle is between the two specified angles (a1 & a2). + /// + /// + /// + /// + /// + /// + public static bool IsBetweenRad(double angle, double a1, double a2, bool reversed = false) + { + if (reversed) + Generic.Swap(ref a1, ref a2); + + var diff = Angle.NormalizeRad(a2 - a1); + + // full circle + if (a2.IsEqualTo(a1)) + return true; + + a1 = Angle.NormalizeRad(angle - a1); + a2 = Angle.NormalizeRad(a2 - angle); + + return diff >= a1 - Tolerance.Epsilon || + diff >= a2 - Tolerance.Epsilon; + } + + /// + /// Whether the given angle is between the two specified angles (a1 & a2). + /// + /// + /// + /// + /// + /// + public static bool IsBetweenDeg(double angle, double a1, double a2, bool reversed = false) + { + if (reversed) + Generic.Swap(ref a1, ref a2); + + var diff = Angle.NormalizeRad(a2 - a1); + + // full circle + if (a2.IsEqualTo(a1)) + return true; + + a1 = Angle.NormalizeRad(angle - a1); + a2 = Angle.NormalizeRad(a2 - angle); + + return diff >= a1 - Tolerance.Epsilon || + diff >= a2 - Tolerance.Epsilon; + } + } +} diff --git a/Source/OpenNest.Core/BoundingBox.cs b/Source/OpenNest.Core/BoundingBox.cs new file mode 100644 index 0000000..5c94b75 --- /dev/null +++ b/Source/OpenNest.Core/BoundingBox.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest +{ + public static class BoundingBox + { + public static Box GetBoundingBox(this IList boxes) + { + if (boxes.Count == 0) + return Box.Empty; + + double minX = boxes[0].X; + double minY = boxes[0].Y; + double maxX = boxes[0].X + boxes[0].Width; + double maxY = boxes[0].Y + boxes[0].Height; + + foreach (var box in boxes) + { + if (box.Left < minX) minX = box.Left; + if (box.Right > maxX) maxX = box.Right; + if (box.Bottom < minY) minY = box.Bottom; + if (box.Top > maxY) maxY = box.Top; + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + + public static Box GetBoundingBox(this IList pts) + { + if (pts.Count == 0) + return Box.Empty; + + var first = pts[0]; + var minX = first.X; + var maxX = first.X; + var minY = first.Y; + var maxY = first.Y; + + for (int i = 1; i < pts.Count; ++i) + { + var vertex = pts[i]; + + if (vertex.X < minX) minX = vertex.X; + else if (vertex.X > maxX) maxX = vertex.X; + + if (vertex.Y < minY) minY = vertex.Y; + else if (vertex.Y > maxY) maxY = vertex.Y; + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + + public static Box GetBoundingBox(this IEnumerable items) + { + var first = items.FirstOrDefault(); + + if (first == null) + return Box.Empty; + + double left = first.Left; + double bottom = first.Bottom; + double right = first.Right; + double top = first.Top; + + foreach (var box in items) + { + if (box.Left < left) left = box.Left; + if (box.Right > right) right = box.Right; + if (box.Bottom < bottom) bottom = box.Bottom; + if (box.Top > top) top = box.Top; + } + + return new Box(left, bottom, right - left, top - bottom); + } + } +} diff --git a/Source/OpenNest.Core/Box.cs b/Source/OpenNest.Core/Box.cs new file mode 100644 index 0000000..12710de --- /dev/null +++ b/Source/OpenNest.Core/Box.cs @@ -0,0 +1,206 @@ +namespace OpenNest +{ + public class Box + { + public static readonly Box Empty = new Box(); + + public Box() + : this(0, 0, 0, 0) + { + } + + public Box(double x, double y, double w, double h) + { + Location = new Vector(x, y); + Width = w; + Height = h; + } + + public Vector Location; + + public Vector Center + { + get { return new Vector(X + Width * 0.5, Y + Height * 0.5); } + } + + public Size Size; + + public double X + { + get { return Location.X; } + set { Location.X = value; } + } + + public double Y + { + get { return Location.Y; } + set { Location.Y = value; } + } + + public double Width + { + get { return Size.Width; } + set { Size.Width = value; } + } + + public double Height + { + get { return Size.Height; } + set { Size.Height = value; } + } + + public void MoveTo(double x, double y) + { + X = x; + Y = y; + } + + public void MoveTo(Vector pt) + { + X = pt.X; + Y = pt.Y; + } + + public void Offset(double x, double y) + { + X += x; + Y += y; + } + + public void Offset(Vector voffset) + { + Location += voffset; + } + + public double Left + { + get { return X; } + } + + public double Right + { + get { return X + Width; } + } + + public double Top + { + get { return Y + Height; } + } + + public double Bottom + { + get { return Y; } + } + + public double Area() + { + return Width * Height; + } + + public double Perimeter() + { + return Width * 2 + Height * 2; + } + + public bool Intersects(Box box) + { + if (Left >= box.Right) return false; + if (Right <= box.Left) return false; + if (Top <= box.Bottom) return false; + if (Bottom >= box.Top) return false; + + return true; + } + + public bool Intersects(Box box, out Box intersectingBox) + { + if (!Intersects(box)) + { + intersectingBox = Box.Empty; + return false; + } + + var left = Left < box.Left ? box.Left : Left; + var right = Right < box.Right ? Right : box.Right; + + var bottom = Bottom < box.Bottom ? box.Bottom : Bottom; + var top = Top < box.Top ? Top : box.Top; + + intersectingBox = new Box(left, bottom, right - left, top - bottom); + + return true; + } + + public bool Contains(Box box) + { + if (box.Top > Top) return false; + if (box.Left < Left) return false; + if (box.Right > Right) return false; + if (box.Bottom < Bottom) return false; + + return true; + } + + public bool Contains(Vector pt) + { + return pt.X >= Left - Tolerance.Epsilon && pt.X <= Right + Tolerance.Epsilon + && pt.Y >= Bottom - Tolerance.Epsilon && pt.Y <= Top + Tolerance.Epsilon; + } + + public bool IsHorizontalTo(Box box) + { + return Bottom > box.Top || Top < box.Bottom; + } + + public bool IsHorizontalTo(Box box, out RelativePosition pos) + { + if (Bottom >= box.Top || Top <= box.Bottom) + { + pos = RelativePosition.None; + return false; + } + + if (Left >= box.Right) + pos = RelativePosition.Right; + else if (Right <= box.Left) + pos = RelativePosition.Left; + else + pos = RelativePosition.Intersecting; + + return true; + } + + public bool IsVerticalTo(Box box) + { + return Left > box.Right || Right < box.Left; + } + + public bool IsVerticalTo(Box box, out RelativePosition pos) + { + if (Left >= box.Right || Right <= box.Left) + { + pos = RelativePosition.None; + return false; + } + + if (Bottom >= box.Top) + pos = RelativePosition.Top; + else if (Top <= box.Bottom) + pos = RelativePosition.Bottom; + else + pos = RelativePosition.Intersecting; + + return true; + } + + public Box Offset(double d) + { + return new Box(X - d, Y - d, Width + d * 2, Height + d * 2); + } + + public override string ToString() + { + return string.Format("[Box: X={0}, Y={1}, Width={2}, Height={3}]", X, Y, Width, Height); + } + } +} diff --git a/Source/OpenNest.Core/BoxSplitter.cs b/Source/OpenNest.Core/BoxSplitter.cs new file mode 100644 index 0000000..30a2d7c --- /dev/null +++ b/Source/OpenNest.Core/BoxSplitter.cs @@ -0,0 +1,57 @@ +namespace OpenNest +{ + public static class BoxSplitter + { + public static Box SplitTop(Box large, Box small) + { + if (!large.Intersects(small)) + return Box.Empty; + + var x = large.Left; + var y = small.Top; + var w = large.Width; + var h = large.Top - y; + + return new Box(x, y, w, h); + } + + public static Box SplitLeft(Box large, Box small) + { + if (!large.Intersects(small)) + return Box.Empty; + + var x = large.Left; + var y = large.Bottom; + var w = small.Left - x; + var h = large.Height; + + return new Box(x, y, w, h); + } + + public static Box SplitBottom(Box large, Box small) + { + if (!large.Intersects(small)) + return Box.Empty; + + var x = large.Left; + var y = large.Bottom; + var w = large.Width; + var h = small.Top - y; + + return new Box(x, y, w, h); + } + + public static Box SplitRight(Box large, Box small) + { + if (!large.Intersects(small)) + return Box.Empty; + + var x = small.Right; + var y = large.Bottom; + var w = large.Right - x; + var h = large.Height; + + return new Box(x, y, w, h); + } + } +} diff --git a/Source/OpenNest.Core/CNC/CircularMove.cs b/Source/OpenNest.Core/CNC/CircularMove.cs new file mode 100644 index 0000000..d1a4044 --- /dev/null +++ b/Source/OpenNest.Core/CNC/CircularMove.cs @@ -0,0 +1,89 @@ + +namespace OpenNest.CNC +{ + public class CircularMove : Motion + { + public CircularMove() + { + } + + public CircularMove(double x, double y, double i, double j, RotationType rotation = RotationType.CCW) + : this(new Vector(x, y), new Vector(i, j), rotation) + { + } + + public CircularMove(Vector endPoint, Vector centerPoint, RotationType rotation = RotationType.CCW) + { + EndPoint = endPoint; + CenterPoint = centerPoint; + Rotation = rotation; + Layer = LayerType.Cut; + } + + public LayerType Layer { get; set; } + + public RotationType Rotation { get; set; } + + public Vector CenterPoint { get; set; } + + public double Radius + { + get { return CenterPoint.DistanceTo(EndPoint); } + } + + public override void Rotate(double angle) + { + base.Rotate(angle); + CenterPoint = CenterPoint.Rotate(angle); + } + + public override void Rotate(double angle, Vector origin) + { + base.Rotate(angle, origin); + CenterPoint = CenterPoint.Rotate(angle, origin); + } + + public override void Offset(double x, double y) + { + base.Offset(x, y); + CenterPoint = new Vector(CenterPoint.X + x, CenterPoint.Y + y); + } + + public override void Offset(Vector voffset) + { + base.Offset(voffset); + CenterPoint += voffset; + } + + public override CodeType Type + { + get { return CodeType.CircularMove; } + } + + public override ICode Clone() + { + return new CircularMove(EndPoint, CenterPoint, Rotation) + { + Layer = Layer + }; + } + + public override string ToString() + { + return ToString(DefaultDecimalPlaces); + } + + public override string ToString(int decimalPlaces) + { + var dp = "N" + decimalPlaces; + var x = EndPoint.X.ToString(dp); + var y = EndPoint.Y.ToString(dp); + var i = CenterPoint.X.ToString(dp); + var j = CenterPoint.Y.ToString(dp); + + return Rotation == RotationType.CW ? + string.Format("G02 X{0} Y{1} I{2} J{3}", x, y, i, j) : + string.Format("G03 X{0} Y{1} I{2} J{3}", x, y, i, j); + } + } +} diff --git a/Source/OpenNest.Core/CNC/CodeType.cs b/Source/OpenNest.Core/CNC/CodeType.cs new file mode 100644 index 0000000..f79b4c7 --- /dev/null +++ b/Source/OpenNest.Core/CNC/CodeType.cs @@ -0,0 +1,14 @@ + +namespace OpenNest.CNC +{ + public enum CodeType + { + CircularMove, + Comment, + LinearMove, + RapidMove, + SetFeedrate, + SetKerf, + SubProgramCall + } +} diff --git a/Source/OpenNest.Core/CNC/Comment.cs b/Source/OpenNest.Core/CNC/Comment.cs new file mode 100644 index 0000000..b2aa4dd --- /dev/null +++ b/Source/OpenNest.Core/CNC/Comment.cs @@ -0,0 +1,31 @@ +namespace OpenNest.CNC +{ + public class Comment : ICode + { + public Comment() + { + } + + public Comment(string value) + { + Value = value; + } + + public string Value { get; set; } + + public CodeType Type + { + get { return CodeType.Comment; } + } + + public ICode Clone() + { + return new Comment(Value); + } + + public override string ToString() + { + return ':' + Value; + } + } +} diff --git a/Source/OpenNest.Core/CNC/Feedrate.cs b/Source/OpenNest.Core/CNC/Feedrate.cs new file mode 100644 index 0000000..03e177c --- /dev/null +++ b/Source/OpenNest.Core/CNC/Feedrate.cs @@ -0,0 +1,35 @@ +namespace OpenNest.CNC +{ + public class Feedrate : ICode + { + public const int UseDefault = -1; + + public const int UseMax = -2; + + public Feedrate() + { + } + + public Feedrate(double value) + { + Value = value; + } + + public double Value { get; set; } + + public CodeType Type + { + get { return CodeType.SetFeedrate; } + } + + public ICode Clone() + { + return new Feedrate(Value); + } + + public override string ToString() + { + return string.Format("F{0}", Value); + } + } +} diff --git a/Source/OpenNest.Core/CNC/ICode.cs b/Source/OpenNest.Core/CNC/ICode.cs new file mode 100644 index 0000000..6705aa0 --- /dev/null +++ b/Source/OpenNest.Core/CNC/ICode.cs @@ -0,0 +1,9 @@ +namespace OpenNest.CNC +{ + public interface ICode + { + CodeType Type { get; } + + ICode Clone(); + } +} diff --git a/Source/OpenNest.Core/CNC/Kerf.cs b/Source/OpenNest.Core/CNC/Kerf.cs new file mode 100644 index 0000000..0294c82 --- /dev/null +++ b/Source/OpenNest.Core/CNC/Kerf.cs @@ -0,0 +1,39 @@ +namespace OpenNest.CNC +{ + public class Kerf : ICode + { + public Kerf(KerfType kerf = KerfType.Left) + { + Value = kerf; + } + + public KerfType Value { get; set; } + + public CodeType Type + { + get { return CodeType.SetKerf; } + } + + public ICode Clone() + { + return new Kerf(Value); + } + + public override string ToString() + { + switch (Value) + { + case KerfType.Left: + return "G41"; + + case KerfType.Right: + return "G42"; + + case KerfType.None: + return "G40"; + } + + return string.Empty; + } + } +} diff --git a/Source/OpenNest.Core/CNC/KerfType.cs b/Source/OpenNest.Core/CNC/KerfType.cs new file mode 100644 index 0000000..8341cfa --- /dev/null +++ b/Source/OpenNest.Core/CNC/KerfType.cs @@ -0,0 +1,10 @@ + +namespace OpenNest.CNC +{ + public enum KerfType + { + None, + Left, + Right + } +} diff --git a/Source/OpenNest.Core/CNC/LayerType.cs b/Source/OpenNest.Core/CNC/LayerType.cs new file mode 100644 index 0000000..1d0c0c0 --- /dev/null +++ b/Source/OpenNest.Core/CNC/LayerType.cs @@ -0,0 +1,12 @@ + +namespace OpenNest.CNC +{ + public enum LayerType + { + Display, + Scribe, + Cut, + Leadin, + Leadout + } +} diff --git a/Source/OpenNest.Core/CNC/LinearMove.cs b/Source/OpenNest.Core/CNC/LinearMove.cs new file mode 100644 index 0000000..37905ba --- /dev/null +++ b/Source/OpenNest.Core/CNC/LinearMove.cs @@ -0,0 +1,47 @@ +namespace OpenNest.CNC +{ + public class LinearMove : Motion + { + public LinearMove() + : this(new Vector()) + { + } + + public LinearMove(double x, double y) + : this(new Vector(x, y)) + { + } + + public LinearMove(Vector endPoint) + { + EndPoint = endPoint; + Layer = LayerType.Cut; + } + + public LayerType Layer { get; set; } + + public override CodeType Type + { + get { return CodeType.LinearMove; } + } + + public override ICode Clone() + { + return new LinearMove(EndPoint) + { + Layer = Layer + }; + } + + public override string ToString() + { + return ToString(DefaultDecimalPlaces); + } + + public override string ToString(int decimalPlaces) + { + var dp = "N" + decimalPlaces; + return string.Format("G01 X{0} Y{1}", EndPoint.X.ToString(dp), EndPoint.Y.ToString(dp)); + } + } +} diff --git a/Source/OpenNest.Core/CNC/Mode.cs b/Source/OpenNest.Core/CNC/Mode.cs new file mode 100644 index 0000000..eb59145 --- /dev/null +++ b/Source/OpenNest.Core/CNC/Mode.cs @@ -0,0 +1,9 @@ + +namespace OpenNest.CNC +{ + public enum Mode + { + Absolute, + Incremental + } +} diff --git a/Source/OpenNest.Core/CNC/Motion.cs b/Source/OpenNest.Core/CNC/Motion.cs new file mode 100644 index 0000000..427e0e0 --- /dev/null +++ b/Source/OpenNest.Core/CNC/Motion.cs @@ -0,0 +1,45 @@ + +namespace OpenNest.CNC +{ + public abstract class Motion : ICode + { + protected const int DefaultDecimalPlaces = 4; + + public Vector EndPoint { get; set; } + + public bool UseExactStop { get; set; } + + public int Feedrate { get; set; } + + protected Motion() + { + Feedrate = CNC.Feedrate.UseDefault; + } + + public virtual void Rotate(double angle) + { + EndPoint = EndPoint.Rotate(angle); + } + + public virtual void Rotate(double angle, Vector origin) + { + EndPoint = EndPoint.Rotate(angle, origin); + } + + public virtual void Offset(double x, double y) + { + EndPoint = new Vector(EndPoint.X + x, EndPoint.Y + y); + } + + public virtual void Offset(Vector voffset) + { + EndPoint += voffset; + } + + public abstract CodeType Type { get; } + + public abstract ICode Clone(); + + public abstract string ToString(int decimalPlaces); + } +} diff --git a/Source/OpenNest.Core/CNC/Program.cs b/Source/OpenNest.Core/CNC/Program.cs new file mode 100644 index 0000000..094894a --- /dev/null +++ b/Source/OpenNest.Core/CNC/Program.cs @@ -0,0 +1,477 @@ +using System; +using System.Collections.Generic; +using OpenNest.Geometry; + +namespace OpenNest.CNC +{ + public class Program + { + public List Codes; + + private Mode mode; + + public Program(Mode mode = Mode.Absolute) + { + Codes = new List(); + Mode = mode; + } + + public Mode Mode + { + get { return mode; } + set + { + if (value == Mode.Absolute) + SetModeAbs(); + else + SetModeInc(); + } + } + + public double Rotation { get; protected set; } + + private void SetModeInc() + { + if (mode == Mode.Incremental) + return; + + ConvertMode.ToIncremental(this); + + mode = Mode.Incremental; + } + + private void SetModeAbs() + { + if (mode == Mode.Absolute) + return; + + ConvertMode.ToAbsolute(this); + + mode = Mode.Absolute; + } + + public virtual void Rotate(double angle) + { + var mode = Mode; + + SetModeAbs(); + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + + if (code.Type == CodeType.SubProgramCall) + { + var subpgm = (SubProgramCall)code; + + if (subpgm.Program != null) + subpgm.Program.Rotate(angle); + } + + if (code is Motion == false) + continue; + + var code2 = (Motion)code; + + code2.Rotate(angle); + } + + if (mode == Mode.Incremental) + SetModeInc(); + + Rotation = Angle.NormalizeRad(Rotation + angle); + } + + public virtual void Rotate(double angle, Vector origin) + { + var mode = Mode; + + SetModeAbs(); + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + + if (code.Type == CodeType.SubProgramCall) + { + var subpgm = (SubProgramCall)code; + + if (subpgm.Program != null) + subpgm.Program.Rotate(angle); + } + + if (code is Motion == false) + continue; + + var code2 = (Motion)code; + + code2.Rotate(angle, origin); + } + + if (mode == Mode.Incremental) + SetModeInc(); + + Rotation = Angle.NormalizeRad(Rotation + angle); + } + + public virtual void Offset(double x, double y) + { + var mode = Mode; + + SetModeAbs(); + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + + if (code is Motion == false) + continue; + + var code2 = (Motion)code; + + code2.Offset(x, y); + } + + if (mode == Mode.Incremental) + SetModeInc(); + } + + public virtual void Offset(Vector voffset) + { + var mode = Mode; + + SetModeAbs(); + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + + if (code is Motion == false) + continue; + + var code2 = (Motion)code; + + code2.Offset(voffset); + } + + if (mode == Mode.Incremental) + SetModeInc(); + } + + public void LineTo(double x, double y) + { + Codes.Add(new LinearMove(x, y)); + } + + public void LineTo(Vector pt) + { + Codes.Add(new LinearMove(pt)); + } + + public void MoveTo(double x, double y) + { + Codes.Add(new RapidMove(x, y)); + } + + public void MoveTo(Vector pt) + { + Codes.Add(new RapidMove(pt)); + } + + public void ArcTo(double x, double y, double i, double j, RotationType rotation) + { + Codes.Add(new CircularMove(x, y, i, j, rotation)); + } + + public void ArcTo(Vector endpt, Vector center, RotationType rotation) + { + Codes.Add(new CircularMove(endpt, center, rotation)); + } + + public void AddSubProgram(Program program) + { + Codes.Add(new SubProgramCall(program, program.Rotation)); + } + + public ICode this[int index] + { + get { return Codes[index]; } + set { Codes[index] = value; } + } + + public int Length + { + get { return Codes.Count; } + } + + public void Merge(Program pgm) + { + // Set the program to be merged to the same Mode as the current. + pgm.Mode = this.Mode; + + if (Mode == Mode.Absolute) + { + bool isRapid = false; + + // Check if the first motion code is a rapid move + foreach (var code in pgm.Codes) + { + if (code is Motion == false) + continue; + + var motion = (Motion)code; + + isRapid = motion.GetType() == typeof(RapidMove); + break; + } + + // If the first motion code is not a rapid, move to the origin. + if (!isRapid) + MoveTo(0, 0); + + Codes.AddRange(pgm.Codes); + } + else + { + Codes.AddRange(pgm.Codes); + } + } + + public Vector EndPoint() + { + switch (Mode) + { + case Mode.Absolute: + { + for (int i = Codes.Count; i >= 0; --i) + { + var code = Codes[i]; + var motion = code as Motion; + + if (motion == null) continue; + + return motion.EndPoint; + } + break; + } + + case Mode.Incremental: + { + var pos = new Vector(0, 0); + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + var motion = code as Motion; + + if (motion == null) continue; + + pos += motion.EndPoint; + } + + return pos; + } + } + + return new Vector(0, 0); + } + + public Box BoundingBox() + { + var origin = new Vector(0, 0); + return BoundingBox(ref origin); + } + + private Box BoundingBox(ref Vector pos) + { + double minX = 0.0; + double minY = 0.0; + double maxX = 0.0; + double maxY = 0.0; + + for (int i = 0; i < Codes.Count; ++i) + { + var code = Codes[i]; + + switch (code.Type) + { + case CodeType.LinearMove: + { + var line = (LinearMove)code; + var pt = Mode == Mode.Absolute ? + line.EndPoint : + line.EndPoint + pos; + + if (pt.X > maxX) + maxX = pt.X; + else if (pt.X < minX) + minX = pt.X; + + if (pt.Y > maxY) + maxY = pt.Y; + else if (pt.Y < minY) + minY = pt.Y; + + pos = pt; + + break; + } + + case CodeType.RapidMove: + { + var line = (RapidMove)code; + var pt = Mode == Mode.Absolute + ? line.EndPoint + : line.EndPoint + pos; + + if (pt.X > maxX) + maxX = pt.X; + else if (pt.X < minX) + minX = pt.X; + + if (pt.Y > maxY) + maxY = pt.Y; + else if (pt.Y < minY) + minY = pt.Y; + + pos = pt; + + break; + } + + case CodeType.CircularMove: + { + var arc = (CircularMove)code; + var radius = arc.CenterPoint.DistanceTo(arc.EndPoint); + + Vector endpt; + Vector centerpt; + + if (Mode == Mode.Incremental) + { + endpt = arc.EndPoint + pos; + centerpt = arc.CenterPoint + pos; + } + else + { + endpt = arc.EndPoint; + centerpt = arc.CenterPoint; + } + + double minX1; + double minY1; + double maxX1; + double maxY1; + + if (pos.X < endpt.X) + { + minX1 = pos.X; + maxX1 = endpt.X; + } + else + { + minX1 = endpt.X; + maxX1 = pos.X; + } + + if (pos.Y < endpt.Y) + { + minY1 = pos.Y; + maxY1 = endpt.Y; + } + else + { + minY1 = endpt.Y; + maxY1 = pos.Y; + } + + var startAngle = pos.AngleFrom(centerpt); + var endAngle = endpt.AngleFrom(centerpt); + + // switch the angle to counter clockwise. + if (arc.Rotation == RotationType.CW) + Generic.Swap(ref startAngle, ref endAngle); + + startAngle = Angle.NormalizeRad(startAngle); + endAngle = Angle.NormalizeRad(endAngle); + + if (Angle.IsBetweenRad(Angle.HalfPI, startAngle, endAngle)) + maxY1 = centerpt.Y + radius; + + if (Angle.IsBetweenRad(Math.PI, startAngle, endAngle)) + minX1 = centerpt.X - radius; + + const double oneHalfPI = Math.PI * 1.5; + + if (Angle.IsBetweenRad(oneHalfPI, startAngle, endAngle)) + minY1 = centerpt.Y - radius; + + if (Angle.IsBetweenRad(Angle.TwoPI, startAngle, endAngle)) + maxX1 = centerpt.X + radius; + + if (maxX1 > maxX) + maxX = maxX1; + + if (minX1 < minX) + minX = minX1; + + if (maxY1 > maxY) + maxY = maxY1; + + if (minY1 < minY) + minY = minY1; + + pos = endpt; + + break; + } + + case CodeType.SubProgramCall: + { + var subpgm = (SubProgramCall)code; + var box = subpgm.Program.BoundingBox(ref pos); + + if (box.Left < minX) + minX = box.Left; + + if (box.Right > maxX) + maxX = box.Right; + + if (box.Bottom < minY) + minY = box.Bottom; + + if (box.Top > maxY) + maxY = box.Top; + + break; + } + } + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + + public object Clone() + { + var pgm = new Program() + { + mode = this.mode, + Rotation = this.Rotation + }; + + var codes = new ICode[Length]; + + for (int i = 0; i < Length; ++i) + codes[i] = this.Codes[i].Clone(); + + pgm.Codes.AddRange(codes); + + return pgm; + } + + public List ToGeometry() + { + return ConvertProgram.ToGeometry(this); + } + } +} diff --git a/Source/OpenNest.Core/CNC/RapidMove.cs b/Source/OpenNest.Core/CNC/RapidMove.cs new file mode 100644 index 0000000..24fe08e --- /dev/null +++ b/Source/OpenNest.Core/CNC/RapidMove.cs @@ -0,0 +1,42 @@ + +namespace OpenNest.CNC +{ + public class RapidMove : Motion + { + public RapidMove() + { + Feedrate = CNC.Feedrate.UseMax; + } + + public RapidMove(Vector endPoint) + { + EndPoint = endPoint; + } + + public RapidMove(double x, double y) + { + EndPoint = new Vector(x, y); + } + + public override CodeType Type + { + get { return CodeType.RapidMove; } + } + + public override ICode Clone() + { + return new RapidMove(EndPoint); + } + + public override string ToString() + { + return ToString(DefaultDecimalPlaces); + } + + public override string ToString(int decimalPlaces) + { + var dp = "N" + decimalPlaces; + return string.Format("G00 X{0} Y{1}", EndPoint.X.ToString(dp), EndPoint.Y.ToString(dp)); + } + } +} diff --git a/Source/OpenNest.Core/CNC/SubProgramCall.cs b/Source/OpenNest.Core/CNC/SubProgramCall.cs new file mode 100644 index 0000000..9988242 --- /dev/null +++ b/Source/OpenNest.Core/CNC/SubProgramCall.cs @@ -0,0 +1,87 @@ +namespace OpenNest.CNC +{ + public class SubProgramCall : ICode + { + private double rotation; + private Program program; + + public SubProgramCall() + { + } + + public SubProgramCall(Program program, double rotation) + { + this.program = program; + this.Rotation = rotation; + } + + /// + /// The program ID. + /// + public int Id { get; set; } + + /// + /// Gets or sets the program. + /// + public Program Program + { + get { return program; } + set + { + program = value; + UpdateProgramRotation(); + } + } + + /// + /// Gets or sets the rotation of the program in degrees. + /// + public double Rotation + { + get { return rotation; } + set + { + rotation = value; + UpdateProgramRotation(); + } + } + + /// + /// Rotates the program by the difference of the current + /// rotation set in the sub program call and the program. + /// + private void UpdateProgramRotation() + { + if (program != null) + { + var diffAngle = Angle.ToRadians(rotation) - program.Rotation; + + if (!diffAngle.IsEqualTo(0.0)) + program.Rotate(diffAngle); + } + } + + /// + /// Gets the code type. + /// + /// + public CodeType Type + { + get { return CodeType.SubProgramCall; } + } + + /// + /// Gets a shallow copy. + /// + /// + public ICode Clone() + { + return new SubProgramCall(program, Rotation); + } + + public override string ToString() + { + return string.Format("G65 P{0} R{1}", Id, Rotation); + } + } +} diff --git a/Source/OpenNest.Core/Collections/DrawingCollection.cs b/Source/OpenNest.Core/Collections/DrawingCollection.cs new file mode 100644 index 0000000..3b7969b --- /dev/null +++ b/Source/OpenNest.Core/Collections/DrawingCollection.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace OpenNest.Collections +{ + public class DrawingCollection : HashSet + { + } +} diff --git a/Source/OpenNest.Core/Collections/PartCollection.cs b/Source/OpenNest.Core/Collections/PartCollection.cs new file mode 100644 index 0000000..6a7c7ca --- /dev/null +++ b/Source/OpenNest.Core/Collections/PartCollection.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OpenNest.Collections +{ + public class PartCollection : IList, ICollection, IEnumerable + { + private readonly List parts; + + public event EventHandler PartAdded; + + public event EventHandler PartRemoved; + + public event EventHandler PartChanged; + + public PartCollection() + { + parts = new List(); + } + + public void Add(Part item) + { + var index = parts.Count; + parts.Add(item); + + if (PartAdded != null) + PartAdded.Invoke(this, new PartAddedEventArgs(item, index)); + } + + public void AddRange(IEnumerable collection) + { + var index = parts.Count; + parts.AddRange(collection); + + if (PartAdded != null) + { + foreach (var part in collection) + PartAdded.Invoke(this, new PartAddedEventArgs(part, index++)); + } + } + + public void Insert(int index, Part item) + { + parts.Insert(index, item); + + if (PartAdded != null) + PartAdded.Invoke(this, new PartAddedEventArgs(item, index)); + } + + public bool Remove(Part item) + { + var success = parts.Remove(item); + + if (PartRemoved != null) + PartRemoved.Invoke(this, new PartRemovedEventArgs(item, success)); + + return success; + } + + public void RemoveAt(int index) + { + if (PartRemoved != null) + { + var part = parts[index]; + + parts.RemoveAt(index); + PartRemoved.Invoke(this, new PartRemovedEventArgs(part, true)); + } + else + { + parts.RemoveAt(index); + } + } + + public void Clear() + { + for (int i = parts.Count - 1; i >= 0; --i) + RemoveAt(i); + } + + public int IndexOf(Part item) + { + return parts.IndexOf(item); + } + + public Part this[int index] + { + get + { + return parts[index]; + } + set + { + var old = parts[index]; + + parts[index] = value; + + if (PartChanged != null) + { + var eventArgs = new PartChangedEventArgs(old, value, index); + PartChanged.Invoke(this, eventArgs); + } + } + } + + public bool Contains(Part item) + { + return parts.Contains(item); + } + + public void CopyTo(Part[] array, int arrayIndex) + { + parts.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return parts.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public object ForEach { get; set; } + + public IEnumerator GetEnumerator() + { + return parts.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return parts.GetEnumerator(); + } + } + + public class PartAddedEventArgs : EventArgs + { + public readonly Part Part; + public readonly int Index; + + public PartAddedEventArgs(Part part, int index) + { + Part = part; + Index = index; + } + } + + public class PartRemovedEventArgs : EventArgs + { + public readonly Part Part; + public readonly bool Succeeded; + + public PartRemovedEventArgs(Part part, bool succeeded) + { + Part = part; + Succeeded = succeeded; + } + } + + public class PartChangedEventArgs : EventArgs + { + public readonly Part OldPart; + public readonly Part NewPart; + public readonly int Index; + + public PartChangedEventArgs(Part oldPart, Part newPart, int index) + { + OldPart = oldPart; + NewPart = newPart; + Index = index; + } + } +} diff --git a/Source/OpenNest.Core/Collections/PlateCollection.cs b/Source/OpenNest.Core/Collections/PlateCollection.cs new file mode 100644 index 0000000..c40375e --- /dev/null +++ b/Source/OpenNest.Core/Collections/PlateCollection.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Collections +{ + public class PlateCollection : IList, ICollection, IEnumerable + { + private List plates; + + public event EventHandler PlateAdded; + + public event EventHandler PlateRemoved; + + public event EventHandler PlateChanged; + + public PlateCollection() + { + plates = new List(); + } + + public void Add(Plate item) + { + var index = plates.Count; + plates.Add(item); + + if (PlateAdded != null) + PlateAdded.Invoke(this, new PlateAddedEventArgs(item, index)); + } + + public void AddRange(IEnumerable collection) + { + var index = plates.Count; + plates.AddRange(collection); + + if (PlateAdded != null) + { + foreach (var plate in collection) + PlateAdded.Invoke(this, new PlateAddedEventArgs(plate, index++)); + } + } + + public void Insert(int index, Plate item) + { + plates.Insert(index, item); + + if (PlateAdded != null) + PlateAdded.Invoke(this, new PlateAddedEventArgs(item, index)); + } + + public bool Remove(Plate item) + { + var success = plates.Remove(item); + + if (PlateRemoved != null) + PlateRemoved.Invoke(this, new PlateRemovedEventArgs(item, success)); + + return success; + } + + public void RemoveAt(int index) + { + var plate = plates[index]; + plates.RemoveAt(index); + + if (PlateRemoved != null) + PlateRemoved.Invoke(this, new PlateRemovedEventArgs(plate, true)); + } + + public void Clear() + { + for (int i = plates.Count - 1; i >= 0; --i) + RemoveAt(i); + } + + public int IndexOf(Plate item) + { + return plates.IndexOf(item); + } + + public Plate this[int index] + { + get + { + return plates[index]; + } + set + { + var old = plates[index]; + + plates[index] = value; + + if (PlateChanged != null) + { + var eventArgs = new PlateChangedEventArgs(old, value, index); + PlateChanged.Invoke(this, eventArgs); + } + } + } + + public bool Contains(Plate item) + { + return plates.Contains(item); + } + + public void CopyTo(Plate[] array, int arrayIndex) + { + plates.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return plates.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public IEnumerator GetEnumerator() + { + return plates.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return plates.GetEnumerator(); + } + + public void RemoveEmptyPlates() + { + if (Count < 2) + return; + + for (int i = Count - 1; i >= 0; --i) + { + if (this[i].Parts.Count == 0) + RemoveAt(i); + } + } + + public int TotalCount + { + get { return plates.Sum(plate => plate.Quantity); } + } + } + + public class PlateAddedEventArgs : EventArgs + { + public readonly Plate Plate; + public readonly int Index; + + public PlateAddedEventArgs(Plate plate, int index) + { + Plate = plate; + Index = index; + } + } + + public class PlateChangedEventArgs : EventArgs + { + public readonly Plate OldPlate; + public readonly Plate NewPlate; + public readonly int Index; + + public PlateChangedEventArgs(Plate oldPlate, Plate newPlate, int index) + { + OldPlate = oldPlate; + NewPlate = newPlate; + Index = index; + } + } + + public class PlateRemovedEventArgs : EventArgs + { + public readonly Plate Plate; + public readonly bool Succeeded; + + public PlateRemovedEventArgs(Plate plate, bool succeeded) + { + Plate = plate; + Succeeded = succeeded; + } + } +} diff --git a/Source/OpenNest.Core/ConvertGeometry.cs b/Source/OpenNest.Core/ConvertGeometry.cs new file mode 100644 index 0000000..e984a29 --- /dev/null +++ b/Source/OpenNest.Core/ConvertGeometry.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using OpenNest.CNC; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class ConvertGeometry + { + public static Program ToProgram(IList geometry) + { + var shapes = Helper.GetShapes(geometry); + + if (shapes.Count == 0) + return null; + + var perimeter = shapes[0]; + var area = perimeter.BoundingBox.Area(); + var index = 0; + + for (int i = 1; i < shapes.Count; ++i) + { + var program = shapes[i]; + var area2 = program.BoundingBox.Area(); + if (area2 > area) + { + perimeter = program; + area = area2; + index = i; + } + } + + shapes.RemoveAt(index); + + var pgm = new Program(); + + foreach (var shape in shapes) + { + var subpgm = ToProgram(shape); + pgm.Merge(subpgm); + } + + pgm.Merge(ToProgram(perimeter)); + pgm.Mode = Mode.Incremental; + + return pgm; + } + + public static Program ToProgram(Shape shape) + { + var pgm = new Program(); + var lastpt = new Vector(); + + for (int i = 0; i < shape.Entities.Count; i++) + lastpt = AddEntity(pgm, lastpt, shape.Entities[i]); + + return pgm; + } + + private static Vector AddEntity(Program pgm, Vector lastpt, Entity geo) + { + switch (geo.Type) + { + case EntityType.Arc: + lastpt = AddArc(pgm, lastpt, (Arc)geo); + break; + + case EntityType.Circle: + lastpt = AddCircle(pgm, lastpt, (Circle)geo); + break; + + case EntityType.Line: + lastpt = AddLine(pgm, lastpt, (Line)geo); + break; + } + + return lastpt; + } + + private static Vector AddArc(Program pgm, Vector lastpt, Arc arc) + { + var startpt = arc.StartPoint(); + var endpt = arc.EndPoint(); + + if (startpt != lastpt) + pgm.MoveTo(startpt); + + lastpt = endpt; + + pgm.ArcTo(endpt, arc.Center, arc.IsReversed ? RotationType.CW : RotationType.CCW); + return lastpt; + } + + private static Vector AddCircle(Program pgm, Vector lastpt, Circle circle) + { + var startpt = new Vector(circle.Center.X + circle.Radius, circle.Center.Y); + + if (startpt != lastpt) + pgm.MoveTo(startpt); + + pgm.ArcTo(startpt, circle.Center, RotationType.CCW); + + lastpt = startpt; + return lastpt; + } + + private static Vector AddLine(Program pgm, Vector lastpt, Line line) + { + if (line.StartPoint != lastpt) + pgm.MoveTo(line.StartPoint); + + pgm.LineTo(line.EndPoint); + + lastpt = line.EndPoint; + return lastpt; + } + } +} diff --git a/Source/OpenNest.Core/ConvertMode.cs b/Source/OpenNest.Core/ConvertMode.cs new file mode 100644 index 0000000..8e98859 --- /dev/null +++ b/Source/OpenNest.Core/ConvertMode.cs @@ -0,0 +1,52 @@ +using OpenNest.CNC; + +namespace OpenNest +{ + public static class ConvertMode + { + /// + /// Converts the program to absolute coordinates. + /// Does NOT check program mode before converting. + /// + /// + public static void ToAbsolute(Program pgm) + { + var pos = new Vector(0, 0); + + for (int i = 0; i < pgm.Codes.Count; ++i) + { + var code = pgm.Codes[i]; + var motion = code as Motion; + + if (motion != null) + { + motion.Offset(pos); + pos = motion.EndPoint; + } + } + } + + /// + /// Converts the program to intermental coordinates. + /// Does NOT check program mode before converting. + /// + /// + public static void ToIncremental(Program pgm) + { + var pos = new Vector(0, 0); + + for (int i = 0; i < pgm.Codes.Count; ++i) + { + var code = pgm.Codes[i]; + var motion = code as Motion; + + if (motion != null) + { + var pos2 = motion.EndPoint; + motion.Offset(-pos.X, -pos.Y); + pos = pos2; + } + } + } + } +} diff --git a/Source/OpenNest.Core/ConvertProgram.cs b/Source/OpenNest.Core/ConvertProgram.cs new file mode 100644 index 0000000..8be5f57 --- /dev/null +++ b/Source/OpenNest.Core/ConvertProgram.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using OpenNest.CNC; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class ConvertProgram + { + public static List ToGeometry(Program pgm) + { + var geometry = new List(); + var curpos = new Vector(); + var mode = Mode.Absolute; + + AddProgram(pgm, ref mode, ref curpos, ref geometry); + + return geometry; + } + + private static void AddProgram(Program program, ref Mode mode, ref Vector curpos, ref List geometry) + { + mode = program.Mode; + + for (int i = 0; i < program.Length; ++i) + { + var code = program[i]; + + switch (code.Type) + { + case CodeType.CircularMove: + AddCircularMove((CircularMove)code, ref mode, ref curpos, ref geometry); + break; + + case CodeType.LinearMove: + AddLinearMove((LinearMove)code, ref mode, ref curpos, ref geometry); + break; + + case CodeType.RapidMove: + AddRapidMove((RapidMove)code, ref mode, ref curpos, ref geometry); + break; + + case CodeType.SubProgramCall: + var tmpmode = mode; + var subpgm = (SubProgramCall)code; + var geoProgram = new Shape(); + AddProgram(subpgm.Program, ref mode, ref curpos, ref geoProgram.Entities); + geometry.Add(geoProgram); + mode = tmpmode; + break; + } + } + } + + private static void AddLinearMove(LinearMove linearMove, ref Mode mode, ref Vector curpos, ref List geometry) + { + var pt = linearMove.EndPoint; + + if (mode == Mode.Incremental) + pt += curpos; + + var line = new Line(curpos, pt) + { + Layer = ConvertLayer(linearMove.Layer) + }; + geometry.Add(line); + curpos = pt; + } + + private static void AddRapidMove(RapidMove rapidMove, ref Mode mode, ref Vector curpos, ref List geometry) + { + var pt = rapidMove.EndPoint; + + if (mode == Mode.Incremental) + pt += curpos; + + var line = new Line(curpos, pt) + { + Layer = SpecialLayers.Rapid + }; + geometry.Add(line); + curpos = pt; + } + + private static void AddCircularMove(CircularMove circularMove, ref Mode mode, ref Vector curpos, ref List geometry) + { + var center = circularMove.CenterPoint; + var endpt = circularMove.EndPoint; + + if (mode == Mode.Incremental) + { + endpt += curpos; + center += curpos; + } + + var startAngle = center.AngleTo(curpos); + var endAngle = center.AngleTo(endpt); + + var dx = endpt.X - center.X; + var dy = endpt.Y - center.Y; + + var radius = Math.Sqrt(dx * dx + dy * dy); + var layer = ConvertLayer(circularMove.Layer); + + if (startAngle.IsEqualTo(endAngle)) + geometry.Add(new Circle(center, radius) { Layer = layer }); + else + geometry.Add(new Arc(center, radius, startAngle, endAngle, circularMove.Rotation == RotationType.CW) { Layer = layer }); + + curpos = endpt; + } + + private static Layer ConvertLayer(LayerType layer) + { + switch (layer) + { + case LayerType.Cut: + return SpecialLayers.Cut; + + case LayerType.Display: + return SpecialLayers.Display; + + case LayerType.Leadin: + return SpecialLayers.Leadin; + + case LayerType.Leadout: + return SpecialLayers.Leadout; + + case LayerType.Scribe: + return SpecialLayers.Scribe; + + default: + return new Layer(layer.ToString()); + } + } + } +} diff --git a/Source/OpenNest.Core/CutParameters.cs b/Source/OpenNest.Core/CutParameters.cs new file mode 100644 index 0000000..0f39b78 --- /dev/null +++ b/Source/OpenNest.Core/CutParameters.cs @@ -0,0 +1,15 @@ +using System; + +namespace OpenNest +{ + public class CutParameters + { + public double Feedrate { get; set; } + + public double RapidTravelRate { get; set; } + + public TimeSpan PierceTime { get; set; } + + public Units Units { get; set; } + } +} diff --git a/Source/OpenNest.Core/Drawing.cs b/Source/OpenNest.Core/Drawing.cs new file mode 100644 index 0000000..db6841a --- /dev/null +++ b/Source/OpenNest.Core/Drawing.cs @@ -0,0 +1,117 @@ +using System.Drawing; +using System.Linq; +using OpenNest.CNC; + +namespace OpenNest +{ + public class Drawing + { + private Program program; + + public Drawing() + : this(string.Empty, new Program()) + { + } + + public Drawing(string name) + : this(name, new Program()) + { + } + + public Drawing(string name, Program pgm) + { + Name = name; + Material = new Material(); + Program = pgm; + Constraints = new NestConstraints(); + Source = new SourceInfo(); + } + + public string Name { get; set; } + + public string Customer { get; set; } + + public int Priority { get; set; } + + public Quantity Quantity; + + public Material Material { get; set; } + + public Program Program + { + get { return program; } + set + { + program = value; + UpdateArea(); + } + } + + public Color Color { get; set; } + + public NestConstraints Constraints { get; set; } + + public SourceInfo Source { get; set; } + + public double Area { get; protected set; } + + public void UpdateArea() + { + var geometry = ConvertProgram.ToGeometry(Program).Where(entity => entity.Layer != SpecialLayers.Rapid); + var shapes = Helper.GetShapes(geometry); + + if (shapes.Count == 0) + return; + + var areas = new double[shapes.Count]; + + for (int i = 0; i < shapes.Count; i++) + { + var shape = shapes[i]; + areas[i] = shape.Area(); + } + + int largestAreaIndex = 0; + + for (int i = 1; i < areas.Length; i++) + { + var area = areas[i]; + + if (area > areas[largestAreaIndex]) + largestAreaIndex = i; + } + + var outerArea = areas[largestAreaIndex]; + + Area = outerArea - (areas.Sum() - outerArea); + } + + public override bool Equals(object obj) + { + if (obj is Drawing == false) + return false; + + var dwg = (Drawing)obj; + + return Name == dwg.Name; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + } + + public class SourceInfo + { + /// + /// Path to the source file. + /// + public string Path { get; set; } + + /// + /// Offset distances to the original location. + /// + public Vector Offset { get; set; } + } +} diff --git a/Source/OpenNest.Core/EvenOdd.cs b/Source/OpenNest.Core/EvenOdd.cs new file mode 100644 index 0000000..247c043 --- /dev/null +++ b/Source/OpenNest.Core/EvenOdd.cs @@ -0,0 +1,15 @@ +namespace OpenNest +{ + public static class EvenOdd + { + public static bool IsEven(this int i) + { + return (i % 2) == 0; + } + + public static bool IsOdd(this int i) + { + return (i % 2) == 1; + } + } +} diff --git a/Source/OpenNest.Core/Generic.cs b/Source/OpenNest.Core/Generic.cs new file mode 100644 index 0000000..86f0301 --- /dev/null +++ b/Source/OpenNest.Core/Generic.cs @@ -0,0 +1,12 @@ +namespace OpenNest +{ + public static class Generic + { + public static void Swap(ref T a, ref T b) + { + T c = a; + a = b; + b = c; + } + } +} diff --git a/Source/OpenNest.Core/Geometry/Arc.cs b/Source/OpenNest.Core/Geometry/Arc.cs new file mode 100644 index 0000000..31b0cb2 --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Arc.cs @@ -0,0 +1,538 @@ +using System; +using System.Collections.Generic; + +namespace OpenNest.Geometry +{ + public class Arc : Entity + { + private double radius; + private double startAngle; + private double endAngle; + private Vector center; + private bool reversed; + + public Arc() + { + } + + public Arc(double x, double y, double r, double a1, double a2, bool reversed = false) + : this(new Vector(x, y), r, a1, a2, reversed) + { + } + + public Arc(Vector center, double radius, double startAngle, double endAngle, bool reversed = false) + { + this.center = center; + this.radius = radius; + this.startAngle = startAngle; + this.endAngle = endAngle; + this.reversed = reversed; + UpdateBounds(); + } + + /// + /// Center point. + /// + public Vector Center + { + get { return center; } + set + { + var offset = value - center; + boundingBox.Offset(offset); + center = value; + } + } + + /// + /// Arc radius. + /// + public double Radius + { + get { return radius; } + set + { + radius = value; + UpdateBounds(); + } + } + + /// + /// Arc radius * 2. Value NOT stored. + /// + public double Diameter + { + get { return Radius * 2.0; } + set { Radius = value / 2.0; } + } + + /// + /// Start angle in radians. + /// + public double StartAngle + { + get { return startAngle; } + set + { + startAngle = Angle.NormalizeRad(value); + UpdateBounds(); + } + } + + /// + /// End angle in radians. + /// + public double EndAngle + { + get { return endAngle; } + set + { + endAngle = Angle.NormalizeRad(value); + UpdateBounds(); + } + } + + /// + /// Angle in radians between start and end angles. + /// + /// + public double SweepAngle() + { + var startAngle = StartAngle; + var endAngle = EndAngle; + + if (IsReversed) + Generic.Swap(ref startAngle, ref endAngle); + + if (startAngle > endAngle) + startAngle -= Angle.TwoPI; + + return endAngle - startAngle; + } + + /// + /// Gets or sets if the arc direction is reversed (clockwise). + /// + public bool IsReversed + { + get { return reversed; } + set + { + if (reversed != value) + Reverse(); + } + } + + public RotationType Rotation + { + get { return IsReversed ? RotationType.CW : RotationType.CCW; } + set + { + IsReversed = (value == RotationType.CW); + } + } + + /// + /// Start point of the arc. + /// + /// + public Vector StartPoint() + { + return new Vector( + Center.X + Radius * Math.Cos(StartAngle), + Center.Y + Radius * Math.Sin(StartAngle)); + } + + /// + /// End point of the arc. + /// + /// + public Vector EndPoint() + { + return new Vector( + Center.X + Radius * Math.Cos(EndAngle), + Center.Y + Radius * Math.Sin(EndAngle)); + } + + /// + /// Returns true if the given arc has the same center point and radius as this. + /// + /// + /// + public bool IsCoradialTo(Arc arc) + { + return center == arc.Center && Radius.IsEqualTo(arc.Radius); + } + + /// + /// Returns true if the given arc has the same radius as this. + /// + /// + /// + public bool IsConcentricTo(Arc arc) + { + return center == arc.center; + } + + /// + /// Returns true if the given circle has the same radius as this. + /// + /// + /// + public bool IsConcentricTo(Circle circle) + { + return center == circle.Center; + } + + /// + /// Converts the arc to a group of points. + /// + /// Number of parts to divide the arc into. + /// + public List ToPoints(int segments = 1000) + { + var points = new List(); + var stepAngle = reversed + ? -SweepAngle() / segments + : SweepAngle() / segments; + + for (int i = 0; i <= segments; ++i) + { + var angle = stepAngle * i + StartAngle; + + points.Add(new Vector( + Math.Cos(angle) * Radius + Center.X, + Math.Sin(angle) * Radius + Center.Y)); + } + + return points; + } + + /// + /// Linear distance of the arc. + /// + public override double Length + { + get { return Diameter * Math.PI * SweepAngle() / Angle.TwoPI; } + } + + /// + /// Reverses the rotation direction. + /// + public override void Reverse() + { + reversed = !reversed; + Generic.Swap(ref startAngle, ref endAngle); + } + + /// + /// Moves the center point to the given coordinates. + /// + /// The x-coordinate + /// The y-coordinate + public override void MoveTo(double x, double y) + { + Center = new Vector(x, y); + } + + /// + /// Moves the center point to the given point. + /// + /// The new center point location. + public override void MoveTo(Vector pt) + { + Center = pt; + } + + /// + /// Offsets the center point by the given distances. + /// + /// The x-axis offset distance. + /// The y-axis offset distance. + public override void Offset(double x, double y) + { + Center = new Vector(Center.X + x, Center.Y + y); + } + + /// + /// Offsets the center point by the given distances. + /// + /// + public override void Offset(Vector voffset) + { + Center += voffset; + } + + /// + /// Scales the arc from the zero point. + /// + /// + public override void Scale(double factor) + { + center *= factor; + radius *= factor; + UpdateBounds(); + } + + /// + /// Scales the arc from the origin. + /// + /// + /// + public override void Scale(double factor, Vector origin) + { + center = center.Scale(factor, origin); + radius *= factor; + UpdateBounds(); + } + + /// + /// Rotates the arc from the zero point. + /// + /// + public override void Rotate(double angle) + { + startAngle += angle; + endAngle += angle; + center = center.Rotate(angle); + UpdateBounds(); + } + + /// + /// Rotates the arc from the origin. + /// + /// + /// + public override void Rotate(double angle, Vector origin) + { + startAngle += angle; + endAngle += angle; + center = center.Rotate(angle, origin); + UpdateBounds(); + } + + /// + /// Updates the bounding box. + /// + public override void UpdateBounds() + { + var startpt = StartPoint(); + var endpt = EndPoint(); + + double minX; + double minY; + double maxX; + double maxY; + + if (startpt.X < endpt.X) + { + minX = startpt.X; + maxX = endpt.X; + } + else + { + minX = endpt.X; + maxX = startpt.X; + } + + if (startpt.Y < endpt.Y) + { + minY = startpt.Y; + maxY = endpt.Y; + } + else + { + minY = endpt.Y; + maxY = startpt.Y; + } + + var angle1 = StartAngle; + var angle2 = EndAngle; + + // switch the angle to counter clockwise. + if (IsReversed) + Generic.Swap(ref angle1, ref angle2); + + if (Angle.IsBetweenRad(Angle.HalfPI, angle1, angle2)) + maxY = Center.Y + Radius; + + if (Angle.IsBetweenRad(Math.PI, angle1, angle2)) + minX = Center.X - Radius; + + const double oneHalfPI = Math.PI * 1.5; + + if (Angle.IsBetweenRad(oneHalfPI, angle1, angle2)) + minY = Center.Y - Radius; + + if (Angle.IsBetweenRad(Angle.TwoPI, angle1, angle2)) + maxX = Center.X + Radius; + + boundingBox.X = minX; + boundingBox.Y = minY; + boundingBox.Width = maxX - minX; + boundingBox.Height = maxY - minY; + } + + public override Entity OffsetEntity(double distance, OffsetSide side) + { + if (side == OffsetSide.Left && reversed) + { + return new Arc(center, radius + distance, startAngle, endAngle, reversed); + } + else + { + if (distance >= radius) + return null; + + return new Arc(center, radius - distance, startAngle, endAngle, reversed); + } + } + + public override Entity OffsetEntity(double distance, Vector pt) + { + throw new NotImplementedException(); + } + + /// + /// Gets the closest point on the arc to the given point. + /// + /// + /// + public override Vector ClosestPointTo(Vector pt) + { + var angle = Center.AngleTo(pt); + + if (Angle.IsBetweenRad(angle, StartAngle, EndAngle, IsReversed)) + { + return new Vector( + Math.Cos(angle) * Radius + Center.X, + Math.Sin(angle) * Radius + Center.Y); + } + else + { + var sp = StartPoint(); + var ep = EndPoint(); + + return pt.DistanceTo(sp) <= pt.DistanceTo(ep) ? sp : ep; + } + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public override bool Intersects(Arc arc) + { + List pts; + return Helper.Intersects(this, arc, out pts); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Arc arc, out List pts) + { + return Helper.Intersects(this, arc, out pts); ; + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public override bool Intersects(Circle circle) + { + List pts; + return Helper.Intersects(this, circle, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Circle circle, out List pts) + { + return Helper.Intersects(this, circle, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public override bool Intersects(Line line) + { + List pts; + return Helper.Intersects(this, line, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Line line, out List pts) + { + return Helper.Intersects(this, line, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public override bool Intersects(Polygon polygon) + { + List pts; + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Polygon polygon, out List pts) + { + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public override bool Intersects(Shape shape) + { + List pts; + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Shape shape, out List pts) + { + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Type of entity. + /// + public override EntityType Type + { + get { return EntityType.Arc; } + } + } +} diff --git a/Source/OpenNest.Core/Geometry/Circle.cs b/Source/OpenNest.Core/Geometry/Circle.cs new file mode 100644 index 0000000..1ee74de --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Circle.cs @@ -0,0 +1,415 @@ +using System; +using System.Collections.Generic; + +namespace OpenNest.Geometry +{ + public class Circle : Entity + { + private Vector center; + private double radius; + + public Circle() + { + } + + public Circle(double x, double y, double radius) + : this(new Vector(x, y), radius) + { + } + + public Circle(Vector center, double radius) + { + this.center = center; + this.radius = radius; + this.Rotation = RotationType.CCW; + UpdateBounds(); + } + + /// + /// Creates a circle from two points. + /// + /// + /// + /// + public static Circle CreateFrom2Points(Vector pt1, Vector pt2) + { + var line = new Line(pt1, pt2); + return new Circle(line.MidPoint, line.Length * 0.5); + } + + /// + /// Center point of the circle. + /// + public Vector Center + { + get { return center; } + set + { + var offset = value - center; + boundingBox.Offset(offset); + center = value; + } + } + + /// + /// Radius of the circle. + /// + public double Radius + { + get { return radius; } + set + { + radius = value; + UpdateBounds(); + } + } + + /// + /// Radius * 2. Value NOT stored. + /// + public double Diameter + { + get { return Radius * 2.0; } + set { Radius = value / 2.0; } + } + + /// + /// Rotation direction. + /// + public RotationType Rotation { get; set; } + + /// + /// Area of the circle. + /// + /// + public double Area() + { + return Math.PI * Radius * Radius; + } + + /// + /// Linear distance around the circle. + /// + /// + public double Circumference() + { + return Math.PI * Diameter; + } + + /// + /// Returns true if the given circle has the same radius as this. + /// + /// + /// + public bool IsConcentricTo(Circle circle) + { + return center == circle.center; + } + + /// + /// Returns true if the given circle has the same radius as this. + /// + /// + /// + public bool IsConcentricTo(Arc arc) + { + return center == arc.Center; + } + + public bool ContainsPoint(Vector pt) + { + return Center.DistanceTo(pt) <= Radius; + } + + public List ToPoints(int segments = 1000) + { + var points = new List(); + var stepAngle = Angle.TwoPI / segments; + + for (int i = 0; i <= segments; ++i) + { + var angle = stepAngle * i; + + points.Add(new Vector( + Math.Cos(angle) * Radius + Center.X, + Math.Sin(angle) * Radius + Center.Y)); + } + + return points; + } + + /// + /// Linear distance around the circle. + /// + public override double Length + { + get { return Circumference(); } + } + + /// + /// Reverses the rotation direction. + /// + public override void Reverse() + { + if (Rotation == RotationType.CCW) + Rotation = RotationType.CW; + else + Rotation = RotationType.CCW; + } + + /// + /// Moves the center point to the given coordinates. + /// + /// + /// + public override void MoveTo(double x, double y) + { + Center = new Vector(x, y); + } + + /// + /// Moves the center point to the given point. + /// + /// + public override void MoveTo(Vector pt) + { + Center = pt; + } + + /// + /// Offsets the center point by the given distances. + /// + /// + /// + public override void Offset(double x, double y) + { + Center = new Vector(Center.X + x, Center.Y + y); + } + + /// + /// Offsets the center point by the given distances. + /// + /// + public override void Offset(Vector voffset) + { + Center += voffset; + } + + /// + /// Scales the circle from the zero point. + /// + /// + public override void Scale(double factor) + { + center *= factor; + radius *= factor; + UpdateBounds(); + } + + /// + /// Scales the circle from the origin. + /// + /// + /// + public override void Scale(double factor, Vector origin) + { + center = center.Scale(factor, origin); + radius *= factor; + UpdateBounds(); + } + + /// + /// Rotates the circle from the zero point. + /// + /// + public override void Rotate(double angle) + { + Center = Center.Rotate(angle); + } + + /// + /// /// Rotates the circle from the origin. + /// + /// + /// + public override void Rotate(double angle, Vector origin) + { + Center = Center.Rotate(angle, origin); + } + + /// + /// Updates the bounding box. + /// + public override void UpdateBounds() + { + boundingBox.X = Center.X - Radius; + boundingBox.Y = Center.Y - Radius; + boundingBox.Width = Diameter; + boundingBox.Height = Diameter; + } + + public override Entity OffsetEntity(double distance, OffsetSide side) + { + if (side == OffsetSide.Left && Rotation == RotationType.CCW) + { + return Radius <= distance ? null : new Circle(center, Radius - distance) + { + Layer = Layer, + Rotation = Rotation + }; + } + else + { + return new Circle(center, Radius + distance) { Layer = Layer }; + } + } + + public override Entity OffsetEntity(double distance, Vector pt) + { + if (ContainsPoint(pt)) + { + return Radius <= distance ? null : new Circle(center, Radius - distance) + { + Layer = Layer, + Rotation = Rotation + }; + } + else + { + return new Circle(center, Radius + distance) { Layer = Layer }; + } + } + + /// + /// Gets the closest point on the circle to the specified point. + /// + /// + /// + public override Vector ClosestPointTo(Vector pt) + { + var angle = Center.AngleTo(pt); + + return new Vector( + Math.Cos(angle) * Radius + Center.X, + Math.Sin(angle) * Radius + Center.Y); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public override bool Intersects(Arc arc) + { + List pts; + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Arc arc, out List pts) + { + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public override bool Intersects(Circle circle) + { + var dist = Center.DistanceTo(circle.Center); + return (dist < (Radius + circle.Radius) && dist > Math.Abs(Radius - circle.Radius)); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Circle circle, out List pts) + { + return Helper.Intersects(this, circle, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public override bool Intersects(Line line) + { + List pts; + return Helper.Intersects(this, line, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Line line, out List pts) + { + return Helper.Intersects(this, line, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public override bool Intersects(Polygon polygon) + { + List pts; + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Polygon polygon, out List pts) + { + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public override bool Intersects(Shape shape) + { + List pts; + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// Points of intersection. + /// + public override bool Intersects(Shape shape, out List pts) + { + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Type of entity. + /// + public override EntityType Type + { + get { return EntityType.Circle; } + } + } +} diff --git a/Source/OpenNest.Core/Geometry/DefinedShape.cs b/Source/OpenNest.Core/Geometry/DefinedShape.cs new file mode 100644 index 0000000..369f651 --- /dev/null +++ b/Source/OpenNest.Core/Geometry/DefinedShape.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; + +namespace OpenNest.Geometry +{ + public class DefinedShape + { + public DefinedShape(Shape shape) + { + Update(shape.Entities); + } + + public DefinedShape(List entities) + { + Update(entities); + } + + private void Update(List entities) + { + var shapes = Helper.GetShapes(entities); + + Perimeter = shapes[0]; + Cutouts = new List(); + + for (int i = 1; i < shapes.Count; i++) + { + if (shapes[i].Left < Perimeter.Left) + { + Cutouts.Add(Perimeter); + Perimeter = shapes[i]; + } + else + { + Cutouts.Add(shapes[i]); + } + } + } + + public Shape Perimeter { get; set; } + + public List Cutouts { get; set; } + } +} diff --git a/Source/OpenNest.Core/Geometry/Entity.cs b/Source/OpenNest.Core/Geometry/Entity.cs new file mode 100644 index 0000000..20aef49 --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Entity.cs @@ -0,0 +1,268 @@ +using System.Collections.Generic; + +namespace OpenNest.Geometry +{ + public abstract class Entity : IBoundable + { + protected Box boundingBox; + + protected Entity() + { + Layer = OpenNest.Geometry.Layer.Default; + boundingBox = new Box(); + } + + /// + /// Smallest box that contains the entity. + /// + public Box BoundingBox + { + get { return boundingBox; } + } + + /// + /// Entity layer type. + /// + public Layer Layer { get; set; } + + /// + /// X-Coordinate of the left-most point. + /// + public virtual double Left + { + get { return boundingBox.Left; } + } + + /// + /// X-Coordinate of the right-most point. + /// + public virtual double Right + { + get { return boundingBox.Right; } + } + + /// + /// Y-Coordinate of the highest point. + /// + public virtual double Top + { + get { return boundingBox.Top; } + } + + /// + /// Y-Coordinate of the lowest point. + /// + public virtual double Bottom + { + get { return boundingBox.Bottom; } + } + + /// + /// Length of the entity. + /// + public abstract double Length { get; } + + /// + /// Reverses the entity. + /// + public abstract void Reverse(); + + /// + /// Moves the entity location to the given coordinates. + /// + /// + /// + public abstract void MoveTo(double x, double y); + + /// + /// Moves the entity location to the given point. + /// + /// + public abstract void MoveTo(Vector pt); + + /// + /// Offsets the entity location by the given distances. + /// + /// + /// + public abstract void Offset(double x, double y); + + /// + /// Offsets the entity location by the given distances. + /// + /// + public abstract void Offset(Vector voffset); + + /// + /// Scales the entity from the zero point. + /// + /// + public abstract void Scale(double factor); + + /// + /// Scales the entity from the origin. + /// + /// + /// + public abstract void Scale(double factor, Vector origin); + + /// + /// Rotates the entity from the zero point. + /// + /// + public abstract void Rotate(double angle); + + /// + /// Rotates the entity from the origin. + /// + /// + /// + public abstract void Rotate(double angle, Vector origin); + + /// + /// Updates the bounding box. + /// + public abstract void UpdateBounds(); + + /// + /// Gets a new entity offset the given distance from this entity. + /// + /// + /// + /// + public abstract Entity OffsetEntity(double distance, OffsetSide side); + + /// + /// Gets a new entity offset the given distance from this entity. Offset side determined by point. + /// + /// + /// + /// + public abstract Entity OffsetEntity(double distance, Vector pt); + + /// + /// Gets the closest point on the entity to the given point. + /// + /// + /// + public abstract Vector ClosestPointTo(Vector pt); + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public abstract bool Intersects(Arc arc); + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// List to store the points of intersection. + /// + public abstract bool Intersects(Arc arc, out List pts); + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public abstract bool Intersects(Circle circle); + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + /// + public abstract bool Intersects(Circle circle, out List pts); + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public abstract bool Intersects(Line line); + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + /// + public abstract bool Intersects(Line line, out List pts); + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public abstract bool Intersects(Polygon polygon); + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + /// + public abstract bool Intersects(Polygon polygon, out List pts); + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public abstract bool Intersects(Shape shape); + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + /// + public abstract bool Intersects(Shape shape, out List pts); + + /// + /// Type of entity. + /// + public abstract EntityType Type { get; } + } + + public static class EntityExtensions + { + public static double FindBestRotation(this List entities, double stepAngle, double startAngle = 0, double endAngle = Angle.TwoPI) + { + startAngle = Angle.NormalizeRad(startAngle); + + if (!endAngle.IsEqualTo(Angle.TwoPI)) + endAngle = Angle.NormalizeRad(endAngle); + + if (stepAngle.IsEqualTo(0.0)) + return startAngle; + + entities.ForEach(e => e.Rotate(startAngle)); + + var bestAngle = startAngle; + var bestArea = entities.GetBoundingBox().Area(); + + var steps = startAngle < endAngle + ? (endAngle - startAngle) / stepAngle + : (endAngle + Angle.TwoPI) - startAngle / stepAngle; + + for (int i = 1; i <= steps; ++i) + { + entities.ForEach(e => e.Rotate(stepAngle)); + + var area = entities.GetBoundingBox().Area(); + + if (area < bestArea) + { + bestArea = area; + bestAngle = startAngle + stepAngle * i; + } + } + + return bestAngle; + } + } +} diff --git a/Source/OpenNest.Core/Geometry/EntityType.cs b/Source/OpenNest.Core/Geometry/EntityType.cs new file mode 100644 index 0000000..f0cb14e --- /dev/null +++ b/Source/OpenNest.Core/Geometry/EntityType.cs @@ -0,0 +1,12 @@ + +namespace OpenNest.Geometry +{ + public enum EntityType + { + Arc, + Circle, + Line, + Shape, + Polygon + } +} diff --git a/Source/OpenNest.Core/Geometry/Layer.cs b/Source/OpenNest.Core/Geometry/Layer.cs new file mode 100644 index 0000000..7147a4a --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Layer.cs @@ -0,0 +1,29 @@ +using System.Drawing; + +namespace OpenNest.Geometry +{ + public class Layer + { + public static readonly Layer Default = new Layer("0") + { + Color = Color.White, + IsVisible = true + }; + + public Layer(string name) + { + Name = name; + } + + public string Name { get; set; } + + public bool IsVisible { get; set; } + + public Color Color { get; set; } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Source/OpenNest.Core/Geometry/Line.cs b/Source/OpenNest.Core/Geometry/Line.cs new file mode 100644 index 0000000..e411cea --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Line.cs @@ -0,0 +1,552 @@ +using System; +using System.Collections.Generic; + +namespace OpenNest.Geometry +{ + public class Line : Entity + { + private Vector pt1; + private Vector pt2; + + public Line() + { + } + + public Line(double x1, double y1, double x2, double y2) + : this(new Vector(x1, y1), new Vector(x2, y2)) + { + } + + public Line(Vector startPoint, Vector endPoint) + { + pt1 = startPoint; + pt2 = endPoint; + UpdateBounds(); + } + + /// + /// Start point of the line. + /// + public Vector StartPoint + { + get { return pt1; } + set + { + pt1 = value; + UpdateBounds(); + } + } + + /// + /// Mid-point of the line. + /// + public Vector MidPoint + { + get + { + var x = (pt1.X + pt2.X) * 0.5; + var y = (pt1.Y + pt2.Y) * 0.5; + return new Vector(x, y); + } + } + + /// + /// End point of the line. + /// + public Vector EndPoint + { + get { return pt2; } + set + { + pt2 = value; + UpdateBounds(); + } + } + + /// + /// Gets the point on the line that is perpendicular from the given point. + /// + /// + /// + public Vector PointPerpendicularFrom(Vector pt) + { + var diff1 = pt - StartPoint; + var diff2 = EndPoint - StartPoint; + var dotProduct = diff1.X * diff2.X + diff1.Y * diff2.Y; + var lengthSquared = diff2.X * diff2.X + diff2.Y * diff2.Y; + var param = dotProduct / lengthSquared; + + if (param < 0) + return StartPoint; + else if (param > 1) + return EndPoint; + else + { + return new Vector( + StartPoint.X + param * diff2.X, + StartPoint.Y + param * diff2.Y); + } + } + + /// + /// Returns true if the given line is parallel to this. + /// + /// + /// + public bool IsParallelTo(Line line) + { + bool line1Vertical = IsVertical(); + bool line2Vertical = line.IsVertical(); + + if (line1Vertical) + return line2Vertical; + else if (line2Vertical) + return false; + + return Slope().IsEqualTo(line.Slope()); + } + + /// + /// Returns true if the given line is perpendicular to this. + /// + /// + /// + public bool IsPerpendicularTo(Line line) + { + bool line1Vertical = IsVertical(); + bool line2Vertical = line.IsVertical(); + + if (line1Vertical) + return line.IsHorizontal(); + else if (line.IsVertical()) + return IsHorizontal(); + + return Slope().IsEqualTo(-1 / line.Slope()); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// Point of intersection. + /// + public bool Intersects(Line line, out Vector pt) + { + var a1 = EndPoint.Y - StartPoint.Y; + var b1 = StartPoint.X - EndPoint.X; + var c1 = a1 * StartPoint.X + b1 * StartPoint.Y; + + var a2 = line.EndPoint.Y - line.StartPoint.Y; + var b2 = line.StartPoint.X - line.EndPoint.X; + var c2 = a2 * line.StartPoint.X + b2 * line.StartPoint.Y; + + var d = a1 * b2 - a2 * b1; + + if (d.IsEqualTo(0.0)) + { + pt = new Vector(); + return false; + } + else + { + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + pt = new Vector(x, y); + return boundingBox.Contains(pt) && line.boundingBox.Contains(pt); + } + } + + /// + /// Returns true if this is vertical. + /// + /// + public bool IsVertical() + { + return pt1.X.IsEqualTo(pt2.X); + } + + /// + /// Returns true if this is horizontal. + /// + /// + public bool IsHorizontal() + { + return pt1.Y.IsEqualTo(pt2.Y); + } + + /// + /// Returns true if the given line is collinear to this. + /// + /// + /// + public bool IsCollinearTo(Line line) + { + if (IsVertical()) + { + if (!line.IsVertical()) + return false; + + return StartPoint.X.IsEqualTo(line.StartPoint.X); + } + else if (line.IsVertical()) + return false; + + if (!YIntercept().IsEqualTo(line.YIntercept())) + return false; + + if (!Slope().IsEqualTo(line.Slope())) + return false; + + return true; + } + + /// + /// Angle of the line from start point to end point. + /// + /// + public double Angle() + { + return StartPoint.AngleTo(EndPoint); + } + + /// + /// Returns the angle between the two lines in radians. + /// + /// + /// + public double AngleBetween(Line line) + { + var m1 = Slope(); + var m2 = line.Slope(); + return Math.Atan(Math.Abs((m2 - m1) / (1 + m2 * m1))); + } + + /// + /// Slope of the line. + /// + /// + public double Slope() + { + if (IsVertical()) + throw new DivideByZeroException(); + + return (EndPoint.Y - StartPoint.Y) / (EndPoint.X - StartPoint.X); + } + + /// + /// Gets the y-axis intersection coordinate. + /// + /// + public double YIntercept() + { + return StartPoint.Y - Slope() * StartPoint.X; + } + + /// + /// Length of the line from start point to end point. + /// + public override double Length + { + get + { + var x = EndPoint.X - StartPoint.X; + var y = EndPoint.Y - StartPoint.Y; + return Math.Sqrt(x * x + y * y); + } + } + + /// + /// Reversed the line. + /// + public override void Reverse() + { + Generic.Swap(ref pt1, ref pt2); + } + + /// + /// Moves the start point to the given coordinates. + /// + /// + /// + public override void MoveTo(double x, double y) + { + var xoffset = pt1.X - x; + var yoffset = pt1.Y - y; + + pt2.X += xoffset; + pt2.Y += yoffset; + pt1.X = x; + pt1.Y = y; + boundingBox.Offset(xoffset, yoffset); + } + + /// + /// Moves the start point to the given point. + /// + /// + public override void MoveTo(Vector pt) + { + var offset = pt1 - pt; + + pt2 += offset; + pt1 = pt; + boundingBox.Offset(offset); + } + + /// + /// Offsets the line location by the given distances. + /// + /// + /// + public override void Offset(double x, double y) + { + pt2.X += x; + pt2.Y += y; + pt1.X += x; + pt1.Y += y; + boundingBox.Offset(x, y); + } + + /// + /// Offsets the line location by the given distances. + /// + /// + public override void Offset(Vector voffset) + { + pt1 += voffset; + pt2 += voffset; + boundingBox.Offset(voffset); + } + + /// + /// Scales the line from the zero point. + /// + /// + public override void Scale(double factor) + { + pt1 *= factor; + pt2 *= factor; + } + + /// + /// Scales the line from the origin. + /// + /// + /// + public override void Scale(double factor, Vector origin) + { + pt1 = (pt1 - origin) * factor + origin; + pt2 = (pt2 - origin) * factor + origin; + } + + /// + /// Rotates the line from the zero point. + /// + /// + public override void Rotate(double angle) + { + StartPoint = StartPoint.Rotate(angle); + EndPoint = EndPoint.Rotate(angle); + } + + /// + /// Rotates the line from the origin. + /// + /// + /// + public override void Rotate(double angle, Vector origin) + { + StartPoint = StartPoint.Rotate(angle, origin); + EndPoint = EndPoint.Rotate(angle, origin); + } + + /// + /// Updates the bounding box. + /// + public override sealed void UpdateBounds() + { + if (StartPoint.X < EndPoint.X) + { + boundingBox.X = StartPoint.X; + boundingBox.Width = EndPoint.X - StartPoint.X; + } + else + { + boundingBox.X = EndPoint.X; + boundingBox.Width = StartPoint.X - EndPoint.X; + } + + if (StartPoint.Y < EndPoint.Y) + { + boundingBox.Y = StartPoint.Y; + boundingBox.Height = EndPoint.Y - StartPoint.Y; + } + else + { + boundingBox.Y = EndPoint.Y; + boundingBox.Height = StartPoint.Y - EndPoint.Y; + } + } + + public override Entity OffsetEntity(double distance, OffsetSide side) + { + var angle = OpenNest.Angle.NormalizeRad(Angle() + OpenNest.Angle.HalfPI); + + var x = Math.Cos(angle) * distance; + var y = Math.Sin(angle) * distance; + + var pt = new Vector(x, y); + + return side == OffsetSide.Left + ? new Line(StartPoint + pt, EndPoint + pt) + : new Line(EndPoint + pt, StartPoint + pt); + } + + public override Entity OffsetEntity(double distance, Vector pt) + { + var a = pt - StartPoint; + var b = EndPoint - StartPoint; + var c = a.DotProduct(b); + var side = c < 0 ? OffsetSide.Left : OffsetSide.Right; + + return OffsetEntity(distance, side); + } + + /// + /// Gets the closest point on the line to the given point. + /// + /// + /// + public override Vector ClosestPointTo(Vector pt) + { + var perpendicularPt = PointPerpendicularFrom(pt); + + if (BoundingBox.Contains(perpendicularPt)) + return perpendicularPt; + else + return pt.DistanceTo(StartPoint) <= pt.DistanceTo(EndPoint) ? StartPoint : EndPoint; + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public override bool Intersects(Arc arc) + { + List pts; + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Arc arc, out List pts) + { + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public override bool Intersects(Circle circle) + { + List pts; + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Circle circle, out List pts) + { + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public override bool Intersects(Line line) + { + Vector pt; + return Intersects(line, out pt); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Line line, out List pts) + { + Vector pt; + var success = Helper.Intersects(this, line, out pt); + pts = new List(new[] { pt }); + return success; + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public override bool Intersects(Polygon polygon) + { + List pts; + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Polygon polygon, out List pts) + { + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public override bool Intersects(Shape shape) + { + List pts; + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Shape shape, out List pts) + { + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Type of entity. + /// + public override EntityType Type + { + get { return EntityType.Line; } + } + } +} diff --git a/Source/OpenNest.Core/Geometry/Polygon.cs b/Source/OpenNest.Core/Geometry/Polygon.cs new file mode 100644 index 0000000..6eec98f --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Polygon.cs @@ -0,0 +1,500 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Geometry +{ + public class Polygon : Entity + { + public List Vertices; + + public Polygon() + { + Vertices = new List(); + } + + /// + /// Closes the polygon if it's not already. + /// + public void Close() + { + if (Vertices.Count < 3) + return; + + var first = Vertices.First(); + var last = Vertices.Last(); + + if (first != last) + Vertices.Add(first); + } + + /// + /// Returns true if the polygon is closed. + /// + /// + public bool IsClosed() + { + if (Vertices.Count < 3) + return false; + + return (Vertices.First() == Vertices.Last()); + } + + /// + /// Returns true if the polygon is self intersecting. + /// + /// + public bool IsComplex() + { + var lines = ToLines(); + + for (int i = 0; i < lines.Count; ++i) + { + var line1 = lines[i]; + + for (int j = i; j < lines.Count; ++j) + { + var line2 = lines[j]; + + if (line1.Intersects(line2)) + return true; + } + } + + return false; + } + + /// + /// Area of the polygon. + /// + /// Returns the area or 0 if the polygon is NOT closed. + public double Area() + { + if (Vertices.Count < 3) + return 0.0; + + return Math.Abs(CalculateArea()); + } + + /// + /// Distance around the polygon. + /// + /// + public double Perimeter() + { + if (Vertices.Count < 3) + return 0.0; + + double sum = 0.0; + + var last = Vertices[0]; + + for (int i = 1; i < Vertices.Count; ++i) + { + var current = Vertices[i]; + sum += last.DistanceTo(current); + last = current; + } + + return sum; + } + + /// + /// Gets the rotation direction of the polygon. + /// + /// + public RotationType RotationDirection() + { + if (Vertices.Count < 3) + throw new Exception("Not enough points to determine direction. Must have at least 3 points."); + + return CalculateArea() > 0 ? RotationType.CCW : RotationType.CW; + } + + /// + /// Converts the polygon to a group of lines. + /// + /// + public List ToLines() + { + var list = new List(); + + if (Vertices.Count < 2) + return list; + + var last = Vertices[0]; + + for (int i = 1; i < Vertices.Count; ++i) + { + var current = Vertices[i]; + list.Add(new Line(last, current)); + last = current; + } + + return list; + } + + /// + /// Gets the area of the polygon. + /// + /// + /// Returns the area of the polygon. + /// * Positive number = counter-clockwise rotation + /// * Negative number = clockwise rotation + /// + private double CalculateArea() + { + double xsum = 0; + double ysum = 0; + + for (int i = 0; i < Vertices.Count - 1; ++i) + { + var current = Vertices[i]; + var next = Vertices[i + 1]; + + xsum += current.X * next.Y; + ysum += current.Y * next.X; + } + + return (xsum - ysum) * 0.5; + } + + /// + /// Distance around the polygon. + /// + public override double Length + { + get { return Perimeter(); } + } + + /// + /// Reverses the rotation direction of the polygon. + /// + public override void Reverse() + { + Vertices.Reverse(); + } + + /// + /// Moves the start point to the given coordinates. + /// + /// + /// + public override void MoveTo(double x, double y) + { + if (Vertices.Count == 0) + return; + + var first = Vertices[0]; + var offset = new Vector(x - first.X, y - first.Y); + + Vertices.ForEach(vertex => vertex += offset); + boundingBox.Offset(offset); + } + + /// + /// Moves the start point to the given point. + /// + /// + public override void MoveTo(Vector pt) + { + if (Vertices.Count == 0) + return; + + var first = Vertices[0]; + var offset = pt - first; + + Vertices.ForEach(vertex => vertex += offset); + boundingBox.Offset(offset); + } + + /// + /// Offsets the location by the given distances. + /// + /// + /// + public override void Offset(double x, double y) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] = Vertices[i].Offset(x, y); + + boundingBox.Offset(x, y); + } + + /// + /// Offsets the location by the given distances. + /// + /// + public override void Offset(Vector voffset) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] = Vertices[i].Offset(voffset); + + boundingBox.Offset(voffset); + } + + /// + /// Scales the polygon from the zero point. + /// + /// + public override void Scale(double factor) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] *= factor; + + UpdateBounds(); + } + + /// + /// Scales the polygon from the zero point. + /// + /// + /// + public override void Scale(double factor, Vector origin) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] = (Vertices[i] - origin) * factor + origin; + + UpdateBounds(); + } + + /// + /// Rotates the polygon from the zero point. + /// + /// + public override void Rotate(double angle) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] = Vertices[i].Rotate(angle); + + UpdateBounds(); + } + + /// + /// Rotates the polygon from the origin. + /// + /// + /// + public override void Rotate(double angle, Vector origin) + { + for (int i = 0; i < Vertices.Count; i++) + Vertices[i] = Vertices[i].Rotate(angle, origin); + + UpdateBounds(); + } + + /// + /// Updates the bounding box. + /// + public override void UpdateBounds() + { + if (Vertices.Count == 0) + return; + + var first = Vertices[0]; + var minX = first.X; + var maxX = first.X; + var minY = first.Y; + var maxY = first.Y; + + for (int i = 1; i < Vertices.Count; ++i) + { + var vertex = Vertices[i]; + + if (vertex.X < minX) minX = vertex.X; + else if (vertex.X > maxX) maxX = vertex.X; + + if (vertex.Y < minY) minY = vertex.Y; + else if (vertex.Y > maxY) maxY = vertex.Y; + } + + boundingBox.X = minX; + boundingBox.Y = minY; + boundingBox.Width = maxX - minX; + boundingBox.Height = maxY - minY; + } + + public override Entity OffsetEntity(double distance, OffsetSide side) + { + throw new NotImplementedException(); + } + + public override Entity OffsetEntity(double distance, Vector pt) + { + throw new NotImplementedException(); + } + + /// + /// Gets the closest point on the polygon to the given point. + /// + /// + /// + public override Vector ClosestPointTo(Vector pt) + { + var lines = ToLines(); + + if (lines.Count == 0) + return Vector.Invalid; + + Vector closestPt = lines[0].ClosestPointTo(pt); + double distance = closestPt.DistanceTo(pt); + + for (int i = 1; i < lines.Count; i++) + { + var line = lines[i]; + var closestPt2 = line.ClosestPointTo(pt); + var distance2 = closestPt2.DistanceTo(pt); + + if (distance2 < distance) + { + closestPt = closestPt2; + distance = distance2; + } + } + + return closestPt; + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public override bool Intersects(Arc arc) + { + List pts; + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Arc arc, out List pts) + { + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public override bool Intersects(Circle circle) + { + List pts; + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Circle circle, out List pts) + { + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public override bool Intersects(Line line) + { + List pts; + return Helper.Intersects(line, this, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Line line, out List pts) + { + return Helper.Intersects(line, this, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public override bool Intersects(Polygon polygon) + { + List pts; + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Polygon polygon, out List pts) + { + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public override bool Intersects(Shape shape) + { + List pts; + return Helper.Intersects(shape, this, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Shape shape, out List pts) + { + return Helper.Intersects(shape, this, out pts); + } + + /// + /// Type of entity. + /// + public override EntityType Type + { + get { return EntityType.Polygon; } + } + + internal void Cleanup() + { + for (int i = Vertices.Count - 1; i > 0; i--) + { + var vertex = Vertices[i]; + var nextVertex = Vertices[i - 1]; + + if (vertex == nextVertex) + Vertices.RemoveAt(i); + } + } + + public double FindBestRotation(double stepAngle) + { + var entities = new List(ToLines()); + return entities.FindBestRotation(stepAngle); + } + + public double FindBestRotation(double stepAngle, double startAngle, double endAngle) + { + var entities = new List(ToLines()); + return entities.FindBestRotation(stepAngle, startAngle, endAngle); + } + } +} diff --git a/Source/OpenNest.Core/Geometry/Shape.cs b/Source/OpenNest.Core/Geometry/Shape.cs new file mode 100644 index 0000000..f910a06 --- /dev/null +++ b/Source/OpenNest.Core/Geometry/Shape.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace OpenNest.Geometry +{ + public class Shape : Entity + { + /// + /// Entities that make up the shape. + /// + public List Entities; + + public Shape() + { + Entities = new List(); + } + + /// + /// Returns true if the shape is closed. + /// + /// + public bool IsClosed() + { + if (Entities.Count == 0) + return false; + + var first = Entities[0]; + Vector firstStartPoint; + Vector firstEndPoint; + + switch (first.Type) + { + case EntityType.Arc: + var arc = (Arc)first; + firstStartPoint = arc.StartPoint(); + firstEndPoint = arc.EndPoint(); + break; + + case EntityType.Circle: + return Entities.Count == 1; + + case EntityType.Line: + var line = (Line)first; + firstStartPoint = line.StartPoint; + firstEndPoint = line.EndPoint; + break; + + default: + Debug.Fail("Unhandled geometry type"); + return false; + } + + var endpt = firstEndPoint; + + Entity geo = null; + + for (int i = 1; i < Entities.Count; ++i) + { + geo = Entities[i]; + + switch (geo.Type) + { + case EntityType.Arc: + var arc = (Arc)geo; + + if (arc.StartPoint() != endpt) + return false; + + endpt = arc.EndPoint(); + break; + + case EntityType.Circle: + return Entities.Count == 1; + + case EntityType.Line: + var line = (Line)geo; + + if (line.StartPoint != endpt) + return false; + + endpt = line.EndPoint; + break; + + default: + Debug.Fail("Unhandled geometry type"); + return false; + } + } + + if (geo == null) + return false; + + var last = geo; + Vector lastEndPoint; + + switch (last.Type) + { + case EntityType.Arc: + var arc = (Arc)last; + lastEndPoint = arc.EndPoint(); + break; + + case EntityType.Line: + var line = (Line)last; + lastEndPoint = line.EndPoint; + break; + + default: + Debug.Fail("Unhandled geometry type"); + return false; + } + + return lastEndPoint == firstStartPoint; + } + + /// + /// Gets the area. + /// + /// Returns the area or 0 if the shape is NOT closed. + public double Area() + { + // Check if the shape is closed so we can get the area. + if (!IsClosed()) + return 0; + + // If the shape is closed and only one entity in the geometry + // then that entity would have to be a circle. + if (Entities.Count == 1) + { + var circle = Entities[0] as Circle; + return circle == null ? 0 : circle.Area(); + } + + return ToPolygon().Area(); + } + + /// + /// Joins all overlapping lines and arcs. + /// + public void Optimize() + { + var lines = new List(); + var arcs = new List(); + + foreach (var geo in Entities) + { + switch (geo.Type) + { + case EntityType.Arc: + arcs.Add((Arc)geo); + break; + + case EntityType.Line: + lines.Add((Line)geo); + break; + } + } + + Helper.Optimize(lines); + Helper.Optimize(arcs); + } + + /// + /// Gets the closest point on the shape to the given point. + /// + /// + /// Entity that contains the point. + /// + public Vector ClosestPointTo(Vector pt, out Entity entity) + { + if (Entities.Count == 0) + { + entity = null; + return Vector.Invalid; + } + + var first = Entities[0]; + + Vector closestPt = first.ClosestPointTo(pt); + double distance = closestPt.DistanceTo(pt); + + entity = first; + + for (int i = 1; i < Entities.Count; i++) + { + var entity2 = Entities[i]; + var closestPt2 = entity2.ClosestPointTo(pt); + var distance2 = closestPt2.DistanceTo(pt); + + if (distance2 < distance) + { + closestPt = closestPt2; + distance = distance2; + entity = entity2; + } + } + + return closestPt; + } + + /// + /// Converts the shape to a polygon. + /// + /// + public Polygon ToPolygon(int arcSegments = 1000) + { + var polygon = new Polygon(); + + foreach (var entity in Entities) + { + switch (entity.Type) + { + case EntityType.Arc: + var arc = (Arc)entity; + polygon.Vertices.AddRange(arc.ToPoints(arcSegments)); + break; + + case EntityType.Line: + var line = (Line)entity; + polygon.Vertices.AddRange(new[] + { + line.StartPoint, + line.EndPoint + }); + break; + + case EntityType.Circle: + var circle = (Circle)entity; + polygon.Vertices.AddRange(circle.ToPoints(arcSegments)); + break; + + default: + Debug.Fail("Unhandled geometry type"); + break; + } + } + + polygon.Close(); + polygon.Cleanup(); + + return polygon; + } + + /// + /// Reverses the rotation direction of the shape. + /// + public override void Reverse() + { + Entities.ForEach(e => e.Reverse()); + Entities.Reverse(); + } + + /// + /// Linear distance of the shape. + /// + public override double Length + { + get { return Entities.Sum(geo => geo.Length); } + } + + /// + /// Moves the start point to the given coordinates. + /// + /// + /// + public override void MoveTo(double x, double y) + { + throw new NotImplementedException(); + } + + /// + /// Moves the start point to the given point. + /// + /// + public override void MoveTo(Vector pt) + { + throw new NotImplementedException(); + } + + /// + /// Offsets the shape location by the given distances. + /// + /// + /// + public override void Offset(double x, double y) + { + Entities.ForEach(e => e.Offset(x, y)); + boundingBox.Offset(x, y); + } + + /// + /// Offsets the shape location by the given distances. + /// + /// + public override void Offset(Vector voffset) + { + Entities.ForEach(e => e.Offset(voffset)); + boundingBox.Offset(voffset); + } + + /// + /// Scales the shape from the zero point. + /// + /// + public override void Scale(double factor) + { + Entities.ForEach(e => e.Scale(factor)); + UpdateBounds(); + } + + /// + /// Scales the shape from the origin. + /// + /// + /// + public override void Scale(double factor, Vector origin) + { + Entities.ForEach(e => e.Scale(factor, origin)); + UpdateBounds(); + } + + /// + /// Rotates the shape from the zero point. + /// + /// + public override void Rotate(double angle) + { + Entities.ForEach(e => e.Rotate(angle)); + UpdateBounds(); + } + + /// + /// Rotates the shape from the origin. + /// + /// + /// + public override void Rotate(double angle, Vector origin) + { + Entities.ForEach(e => e.Rotate(angle, origin)); + UpdateBounds(); + } + + /// + /// Updates the bounding box. + /// + public override void UpdateBounds() + { + boundingBox = Entities.Select(geo => geo.BoundingBox) + .ToList() + .GetBoundingBox(); + } + + public override Entity OffsetEntity(double distance, OffsetSide side) + { + var offsetShape = new Shape(); + var definedShape = new DefinedShape(this); + + Entity lastEntity = null; + Entity lastOffsetEntity = null; + + foreach (var entity in definedShape.Perimeter.Entities) + { + var offsetEntity = entity.OffsetEntity(distance, side); + + if (offsetEntity == null) + continue; + + switch (entity.Type) + { + case EntityType.Line: + { + var line = (Line)entity; + var offsetLine = (Line)offsetEntity; + + if (lastOffsetEntity != null && lastOffsetEntity.Type == EntityType.Line) + { + var lastLine = lastEntity as Line; + var lastOffsetLine = lastOffsetEntity as Line; + + if (lastLine == null || lastOffsetLine == null) + continue; + + Vector intersection; + + if (Helper.Intersects(offsetLine, lastOffsetLine, out intersection)) + { + offsetLine.StartPoint = intersection; + lastOffsetLine.EndPoint = intersection; + } + else + { + var arc = new Arc( + line.StartPoint, + distance, + line.StartPoint.AngleTo(lastOffsetLine.EndPoint), + line.StartPoint.AngleTo(offsetLine.StartPoint), + side == OffsetSide.Left + ); + + offsetShape.Entities.Add(arc); + } + } + + offsetShape.Entities.Add(offsetLine); + break; + } + + default: + offsetShape.Entities.Add(offsetEntity); + break; + } + + lastOffsetEntity = offsetEntity; + lastEntity = entity; + } + + foreach (var cutout in definedShape.Cutouts) + offsetShape.Entities.AddRange(((Shape)cutout.OffsetEntity(distance, side)).Entities); + + return offsetShape; + } + + public override Entity OffsetEntity(double distance, Vector pt) + { + throw new NotImplementedException(); + } + + /// + /// Gets the closest point on the shape to the given point. + /// + /// + /// + public override Vector ClosestPointTo(Vector pt) + { + Entity entity; + return ClosestPointTo(pt, out entity); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + public override bool Intersects(Arc arc) + { + List pts; + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given arc is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Arc arc, out List pts) + { + return Helper.Intersects(arc, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + public override bool Intersects(Circle circle) + { + List pts; + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given circle is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Circle circle, out List pts) + { + return Helper.Intersects(circle, this, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + public override bool Intersects(Line line) + { + List pts; + return Helper.Intersects(line, this, out pts); + } + + /// + /// Returns true if the given line is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Line line, out List pts) + { + return Helper.Intersects(line, this, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + public override bool Intersects(Polygon polygon) + { + List pts; + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given polygon is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Polygon polygon, out List pts) + { + return Helper.Intersects(this, polygon, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + public override bool Intersects(Shape shape) + { + List pts; + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Returns true if the given shape is intersecting this. + /// + /// + /// + /// + public override bool Intersects(Shape shape, out List pts) + { + return Helper.Intersects(this, shape, out pts); + } + + /// + /// Type of entity. + /// + public override EntityType Type + { + get { return EntityType.Shape; } + } + + public double FindBestRotation(double stepAngle) + { + return Entities.FindBestRotation(stepAngle); + } + + public double FindBestRotation(double stepAngle, double startAngle, double endAngle) + { + return Entities.FindBestRotation(stepAngle, startAngle, endAngle); + } + } +} diff --git a/Source/OpenNest.Core/Helper.cs b/Source/OpenNest.Core/Helper.cs new file mode 100644 index 0000000..61b43d5 --- /dev/null +++ b/Source/OpenNest.Core/Helper.cs @@ -0,0 +1,990 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class Helper + { + /// + /// Rounds a number down to the nearest factor. + /// + /// + /// + /// + public static double RoundDownToNearest(double num, double factor) + { + return factor.IsEqualTo(0) ? num : Math.Floor(num / factor) * factor; + } + + /// + /// Rounds a number up to the nearest factor. + /// + /// + /// + /// + public static double RoundUpToNearest(double num, double factor) + { + return factor.IsEqualTo(0) ? num : Math.Ceiling(num / factor) * factor; + } + + /// + /// Rounds a number to the nearest factor using midpoint rounding convention. + /// + /// + /// + /// + public static double RoundToNearest(double num, double factor) + { + return factor.IsEqualTo(0) ? num : Math.Round(num / factor) * factor; + } + + public static void Optimize(IList arcs) + { + for (int i = 0; i < arcs.Count; ++i) + { + var arc = arcs[i]; + + var coradialArcs = arcs.GetCoradialArs(arc, i); + int index = 0; + + while (index < coradialArcs.Count) + { + Arc arc2 = coradialArcs[index]; + Arc joinArc; + + if (!TryJoinArcs(arc, arc2, out joinArc)) + { + index++; + continue; + } + + coradialArcs.Remove(arc2); + arcs.Remove(arc2); + + arc = joinArc; + index = 0; + } + + arcs[i] = arc; + } + } + + public static void Optimize(IList lines) + { + for (int i = 0; i < lines.Count; ++i) + { + var line = lines[i]; + + var collinearLines = lines.GetCollinearLines(line, i); + var index = 0; + + while (index < collinearLines.Count) + { + Line line2 = collinearLines[index]; + Line joinLine; + + if (!TryJoinLines(line, line2, out joinLine)) + { + index++; + continue; + } + + collinearLines.Remove(line2); + lines.Remove(line2); + + line = joinLine; + index = 0; + } + + lines[i] = line; + } + } + + public static bool TryJoinLines(Line line1, Line line2, out Line lineOut) + { + lineOut = null; + + if (line1 == line2) + return false; + + if (!line1.IsCollinearTo(line2)) + return false; + + bool onPoint = false; + + if (line1.StartPoint == line2.StartPoint) + onPoint = true; + else if (line1.StartPoint == line2.EndPoint) + onPoint = true; + else if (line1.EndPoint == line2.StartPoint) + onPoint = true; + else if (line1.EndPoint == line2.EndPoint) + onPoint = true; + + var t1 = line1.StartPoint.Y > line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; + var t2 = line2.StartPoint.Y > line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; + var b1 = line1.StartPoint.Y < line1.EndPoint.Y ? line1.StartPoint.Y : line1.EndPoint.Y; + var b2 = line2.StartPoint.Y < line2.EndPoint.Y ? line2.StartPoint.Y : line2.EndPoint.Y; + var l1 = line1.StartPoint.X < line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; + var l2 = line2.StartPoint.X < line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; + var r1 = line1.StartPoint.X > line1.EndPoint.X ? line1.StartPoint.X : line1.EndPoint.X; + var r2 = line2.StartPoint.X > line2.EndPoint.X ? line2.StartPoint.X : line2.EndPoint.X; + + if (!onPoint) + { + if (t1 < b2 - Tolerance.Epsilon) return false; + if (b1 > t2 + Tolerance.Epsilon) return false; + if (l1 > r2 + Tolerance.Epsilon) return false; + if (r1 < l2 - Tolerance.Epsilon) return false; + } + + var l = l1 < l2 ? l1 : l2; + var r = r1 > r2 ? r1 : r2; + var t = t1 > t2 ? t1 : t2; + var b = b1 < b2 ? b1 : b2; + + if (!line1.IsVertical() && line1.Slope() < 0) + lineOut = new Line(new Vector(l, t), new Vector(r, b)); + else + lineOut = new Line(new Vector(l, b), new Vector(r, t)); + + return true; + } + + public static bool TryJoinArcs(Arc arc1, Arc arc2, out Arc arcOut) + { + arcOut = null; + + if (arc1 == arc2) + return false; + + if (arc1.Center != arc2.Center) + return false; + + if (!arc1.Radius.IsEqualTo(arc2.Radius)) + return false; + + if (arc1.StartAngle > arc1.EndAngle) + arc1.StartAngle -= Angle.TwoPI; + + if (arc2.StartAngle > arc2.EndAngle) + arc2.StartAngle -= Angle.TwoPI; + + if (arc1.EndAngle < arc2.StartAngle || arc1.StartAngle > arc2.EndAngle) + return false; + + var startAngle = arc1.StartAngle < arc2.StartAngle ? arc1.StartAngle : arc2.StartAngle; + var endAngle = arc1.EndAngle > arc2.EndAngle ? arc1.EndAngle : arc2.EndAngle; + + if (startAngle < 0) startAngle += Angle.TwoPI; + if (endAngle < 0) endAngle += Angle.TwoPI; + + arcOut = new Arc(arc1.Center, arc1.Radius, startAngle, endAngle); + + return true; + } + + private static List GetCollinearLines(this IList lines, Line line, int startIndex) + { + var collinearLines = new List(); + + Parallel.For(startIndex, lines.Count, index => + { + var compareLine = lines[index]; + + if (Object.ReferenceEquals(line, compareLine)) + return; + + if (!line.IsCollinearTo(compareLine)) + return; + + lock (collinearLines) + { + collinearLines.Add(compareLine); + } + }); + + return collinearLines; + } + + private static List GetCoradialArs(this IList arcs, Arc arc, int startIndex) + { + var coradialArcs = new List(); + + Parallel.For(startIndex, arcs.Count, index => + { + var compareArc = arcs[index]; + + if (Object.ReferenceEquals(arc, compareArc)) + return; + + if (!arc.IsCoradialTo(compareArc)) + return; + + lock (coradialArcs) + { + coradialArcs.Add(compareArc); + } + }); + + return coradialArcs; + } + + public static List GetShapes(IEnumerable entities) + { + var lines = new List(); + var arcs = new List(); + var circles = new List(); + var shapes = new List(); + + var entities2 = new Queue(entities); + + while (entities2.Count > 0) + { + var entity = entities2.Dequeue(); + + switch (entity.Type) + { + case EntityType.Arc: + arcs.Add((Arc)entity); + break; + + case EntityType.Circle: + circles.Add((Circle)entity); + break; + + case EntityType.Line: + lines.Add((Line)entity); + break; + + case EntityType.Shape: + var shape = (Shape)entity; + shape.Entities.ForEach(e => entities2.Enqueue(e)); + break; + + default: + Debug.Fail("Unhandled geometry type"); + break; + } + } + + foreach (var circle in circles) + { + var shape = new Shape(); + shape.Entities.Add(circle); + shape.UpdateBounds(); + shapes.Add(shape); + } + + var entityList = new List(); + + entityList.AddRange(lines); + entityList.AddRange(arcs); + + while (entityList.Count > 0) + { + var next = entityList[0]; + var shape = new Shape(); + shape.Entities.Add(next); + + entityList.RemoveAt(0); + + Vector startPoint = new Vector(); + Entity connected; + + switch (next.Type) + { + case EntityType.Arc: + var arc = (Arc)next; + startPoint = arc.EndPoint(); + break; + + case EntityType.Line: + var line = (Line)next; + startPoint = line.EndPoint; + break; + } + + while ((connected = GetConnected(startPoint, entityList)) != null) + { + shape.Entities.Add(connected); + entityList.Remove(connected); + + switch (connected.Type) + { + case EntityType.Arc: + var arc = (Arc)connected; + startPoint = arc.EndPoint(); + break; + + case EntityType.Line: + var line = (Line)connected; + startPoint = line.EndPoint; + break; + } + } + + shape.UpdateBounds(); + shapes.Add(shape); + } + + return shapes; + } + + internal static Entity GetConnected(Vector pt, IEnumerable geometry) + { + foreach (var geo in geometry) + { + switch (geo.Type) + { + case EntityType.Arc: + var arc = (Arc)geo; + + if (arc.StartPoint() == pt) + return arc; + + if (arc.EndPoint() == pt) + { + arc.Reverse(); + return arc; + } + + break; + + case EntityType.Line: + var line = (Line)geo; + + if (line.StartPoint == pt) + return line; + + if (line.EndPoint == pt) + { + line.Reverse(); + return line; + } + break; + } + } + + return null; + } + + internal static bool Intersects(Arc arc1, Arc arc2, out List pts) + { + var c1 = new Circle(arc1.Center, arc1.Radius); + var c2 = new Circle(arc2.Center, arc2.Radius); + + if (!Intersects(c1, c2, out pts)) + { + pts = new List(); + return false; + } + + pts = pts.Where(pt => + Angle.IsBetweenRad(arc1.Center.AngleTo(pt), arc1.StartAngle, arc1.EndAngle, arc1.IsReversed) && + Angle.IsBetweenRad(arc2.Center.AngleTo(pt), arc2.StartAngle, arc2.EndAngle, arc2.IsReversed)) + .ToList(); + + return pts.Count > 0; + } + + internal static bool Intersects(Arc arc, Circle circle, out List pts) + { + var c1 = new Circle(arc.Center, arc.Radius); + + if (!Intersects(c1, circle, out pts)) + { + pts = new List(); + return false; + } + + pts = pts.Where(pt => Angle.IsBetweenRad( + arc.Center.AngleTo(pt), + arc.StartAngle, + arc.EndAngle, + arc.IsReversed)).ToList(); + + return pts.Count > 0; + } + + internal static bool Intersects(Arc arc, Line line, out List pts) + { + var c1 = new Circle(arc.Center, arc.Radius); + + if (!Intersects(c1, line, out pts)) + { + pts = new List(); + return false; + } + + pts = pts.Where(pt => Angle.IsBetweenRad( + arc.Center.AngleTo(pt), + arc.StartAngle, + arc.EndAngle, + arc.IsReversed)).ToList(); + + return pts.Count > 0; + } + + internal static bool Intersects(Arc arc, Shape shape, out List pts) + { + var pts2 = new List(); + + foreach (var geo in shape.Entities) + { + List pts3; + geo.Intersects(arc, out pts3); + pts2.AddRange(pts3); + } + + pts = pts2.Where(pt => Angle.IsBetweenRad( + arc.Center.AngleTo(pt), + arc.StartAngle, + arc.EndAngle, + arc.IsReversed)).ToList(); + + return pts.Count > 0; + } + + internal static bool Intersects(Arc arc, Polygon polygon, out List pts) + { + var pts2 = new List(); + var lines = polygon.ToLines(); + + foreach (var line in lines) + { + List pts3; + Intersects(arc, line, out pts3); + pts2.AddRange(pts3); + } + + pts = pts2.Where(pt => Angle.IsBetweenRad( + arc.Center.AngleTo(pt), + arc.StartAngle, + arc.EndAngle, + arc.IsReversed)).ToList(); + + return pts.Count > 0; + } + + internal static bool Intersects(Circle circle1, Circle circle2, out List pts) + { + var distance = circle1.Center.DistanceTo(circle2.Center); + + // check if circles are too far apart + if (distance > circle1.Radius + circle2.Radius) + { + pts = new List(); + return false; + } + + // check if one circle contains the other + if (distance < Math.Abs(circle1.Radius - circle2.Radius)) + { + pts = new List(); + return false; + } + + var d = circle2.Center - circle1.Center; + var a = (circle1.Radius * circle1.Radius - circle2.Radius * circle2.Radius + distance * distance) / (2.0 * distance); + var h = Math.Sqrt(circle1.Radius * circle1.Radius - a * a); + + var pt = new Vector( + circle1.Center.X + (a * d.X) / distance, + circle1.Center.Y + (a * d.Y) / distance); + + var i1 = new Vector( + pt.X + (h * d.Y) / distance, + pt.Y - (h * d.X) / distance); + + var i2 = new Vector( + pt.X - (h * d.Y) / distance, + pt.Y + (h * d.X) / distance); + + pts = i1 != i2 ? new List { i1, i2 } : new List { i1 }; + + return true; + } + + internal static bool Intersects(Circle circle, Line line, out List pts) + { + var d1 = line.EndPoint - line.StartPoint; + var d2 = line.StartPoint - circle.Center; + + var a = d1.X * d1.X + d1.Y * d1.Y; + var b = (d1.X * d2.X + d1.Y * d2.Y) * 2; + var c = (d2.X * d2.X + d2.Y * d2.Y) - circle.Radius * circle.Radius; + + var det = b * b - 4 * a * c; + + if ((a <= Tolerance.Epsilon) || (det < 0)) + { + pts = new List(); + return false; + } + + double t; + pts = new List(); + + if (det.IsEqualTo(0)) + { + t = -b / (2 * a); + var pt1 = new Vector(line.StartPoint.X + t * d1.X, line.StartPoint.Y + t * d1.Y); + + if (line.BoundingBox.Contains(pt1)) + pts.Add(pt1); + + return true; + } + + t = (-b + Math.Sqrt(det)) / (2 * a); + var pt2 = new Vector(line.StartPoint.X + t * d1.X, line.StartPoint.Y + t * d1.Y); + + if (line.BoundingBox.Contains(pt2)) + pts.Add(pt2); + + t = (-b - Math.Sqrt(det)) / (2 * a); + var pt3 = new Vector(line.StartPoint.X + t * d1.X, line.StartPoint.Y + t * d1.Y); + + if (line.BoundingBox.Contains(pt3)) + pts.Add(pt3); + + return true; + } + + internal static bool Intersects(Circle circle, Shape shape, out List pts) + { + pts = new List(); + + foreach (var geo in shape.Entities) + { + List pts3; + geo.Intersects(circle, out pts3); + pts.AddRange(pts3); + } + + return pts.Count > 0; + } + + internal static bool Intersects(Circle circle, Polygon polygon, out List pts) + { + pts = new List(); + var lines = polygon.ToLines(); + + foreach (var line in lines) + { + List pts3; + Intersects(circle, line, out pts3); + pts.AddRange(pts3); + } + + return pts.Count > 0; + } + + internal static bool Intersects(Line line1, Line line2, out Vector pt) + { + var a1 = line1.EndPoint.Y - line1.StartPoint.Y; + var b1 = line1.StartPoint.X - line1.EndPoint.X; + var c1 = a1 * line1.StartPoint.X + b1 * line1.StartPoint.Y; + + var a2 = line2.EndPoint.Y - line2.StartPoint.Y; + var b2 = line2.StartPoint.X - line2.EndPoint.X; + var c2 = a2 * line2.StartPoint.X + b2 * line2.StartPoint.Y; + + var d = a1 * b2 - a2 * b1; + + if (d.IsEqualTo(0.0)) + { + pt = Vector.Zero; + return false; + } + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + pt = new Vector(x, y); + return line1.BoundingBox.Contains(pt) && line2.BoundingBox.Contains(pt); + } + + internal static bool Intersects(Line line, Shape shape, out List pts) + { + pts = new List(); + + foreach (var geo in shape.Entities) + { + List pts3; + geo.Intersects(line, out pts3); + pts.AddRange(pts3); + } + + return pts.Count > 0; + } + + internal static bool Intersects(Line line, Polygon polygon, out List pts) + { + pts = new List(); + var lines = polygon.ToLines(); + + foreach (var line2 in lines) + { + Vector pt; + + if (Intersects(line, line2, out pt)) + pts.Add(pt); + } + + return pts.Count > 0; + } + + internal static bool Intersects(Shape shape1, Shape shape2, out List pts) + { + pts = new List(); + + for (int i = 0; i < shape1.Entities.Count; i++) + { + var geo1 = shape1.Entities[i]; + + for (int j = 0; j < shape2.Entities.Count; j++) + { + List pts2; + bool success = false; + + var geo2 = shape2.Entities[j]; + + switch (geo2.Type) + { + case EntityType.Arc: + success = geo1.Intersects((Arc)geo2, out pts2); + break; + + case EntityType.Circle: + success = geo1.Intersects((Circle)geo2, out pts2); + break; + + case EntityType.Line: + success = geo1.Intersects((Line)geo2, out pts2); + break; + + case EntityType.Shape: + success = geo1.Intersects((Shape)geo2, out pts2); + break; + + case EntityType.Polygon: + success = geo1.Intersects((Polygon)geo2, out pts2); + break; + + default: + continue; + } + + if (success) + pts.AddRange(pts2); + } + } + + return pts.Count > 0; + } + + internal static bool Intersects(Shape shape, Polygon polygon, out List pts) + { + pts = new List(); + + var lines = polygon.ToLines(); + + for (int i = 0; i < shape.Entities.Count; i++) + { + var geo = shape.Entities[i]; + + for (int j = 0; j < lines.Count; j++) + { + var line = lines[j]; + + List pts2; + + if (geo.Intersects(line, out pts2)) + pts.AddRange(pts2); + } + } + + return pts.Count > 0; + } + + internal static bool Intersects(Polygon polygon1, Polygon polygon2, out List pts) + { + pts = new List(); + + var lines1 = polygon1.ToLines(); + var lines2 = polygon2.ToLines(); + + for (int i = 0; i < lines1.Count; i++) + { + var line1 = lines1[i]; + + for (int j = 0; j < lines2.Count; j++) + { + var line2 = lines2[j]; + Vector pt; + + if (Intersects(line1, line2, out pt)) + pts.Add(pt); + } + } + + return pts.Count > 0; + } + + public static double ClosestDistanceLeft(Box box, List boxes) + { + var closestDistance = double.MaxValue; + + for (int i = 0; i < boxes.Count; i++) + { + var compareBox = boxes[i]; + + RelativePosition pos; + + if (!box.IsHorizontalTo(compareBox, out pos)) + continue; + + if (pos != RelativePosition.Right) + continue; + + var distance = box.Left - compareBox.Right; + + if (distance < closestDistance) + closestDistance = distance; + } + + return closestDistance == double.MaxValue ? double.NaN : closestDistance; + } + + public static double ClosestDistanceRight(Box box, List boxes) + { + var closestDistance = double.MaxValue; + + for (int i = 0; i < boxes.Count; i++) + { + var compareBox = boxes[i]; + + RelativePosition pos; + + if (!box.IsHorizontalTo(compareBox, out pos)) + continue; + + if (pos != RelativePosition.Left) + continue; + + var distance = compareBox.Left - box.Right; + + if (distance < closestDistance) + closestDistance = distance; + } + + return closestDistance == double.MaxValue ? double.NaN : closestDistance; + } + + public static double ClosestDistanceUp(Box box, List boxes) + { + var closestDistance = double.MaxValue; + + for (int i = 0; i < boxes.Count; i++) + { + var compareBox = boxes[i]; + + RelativePosition pos; + + if (!box.IsVerticalTo(compareBox, out pos)) + continue; + + if (pos != RelativePosition.Bottom) + continue; + + var distance = compareBox.Bottom - box.Top; + + if (distance < closestDistance) + closestDistance = distance; + } + + return closestDistance == double.MaxValue ? double.NaN : closestDistance; + } + + public static double ClosestDistanceDown(Box box, List boxes) + { + var closestDistance = double.MaxValue; + + for (int i = 0; i < boxes.Count; i++) + { + var compareBox = boxes[i]; + + RelativePosition pos; + + if (!box.IsVerticalTo(compareBox, out pos)) + continue; + + if (pos != RelativePosition.Top) + continue; + + var distance = box.Bottom - compareBox.Top; + + if (distance < closestDistance) + closestDistance = distance; + } + + return closestDistance == double.MaxValue ? double.NaN : closestDistance; + } + + public static Box GetLargestBoxVertically(Vector pt, Box bounds, IEnumerable boxes) + { + var verticalBoxes = boxes.Where(b => !(b.Left > pt.X || b.Right < pt.X)).ToList(); + + #region Find Top/Bottom Limits + + var top = double.MaxValue; + var btm = double.MinValue; + + foreach (var box in verticalBoxes) + { + var boxBtm = box.Bottom; + var boxTop = box.Top; + + if (boxBtm > pt.Y && boxBtm < top) + top = boxBtm; + + else if (box.Top < pt.Y && boxTop > btm) + btm = boxTop; + } + + if (top == double.MaxValue) + { + if (bounds.Top > pt.Y) + top = bounds.Top; + else return Box.Empty; + } + + if (btm == double.MinValue) + { + if (bounds.Bottom < pt.Y) + btm = bounds.Bottom; + else return Box.Empty; + } + + #endregion + + var horizontalBoxes = boxes.Where(b => !(b.Bottom >= top || b.Top <= btm)).ToList(); + + #region Find Left/Right Limits + + var lft = double.MinValue; + var rgt = double.MaxValue; + + foreach (var box in horizontalBoxes) + { + var boxLft = box.Left; + var boxRgt = box.Right; + + if (boxLft > pt.X && boxLft < rgt) + rgt = boxLft; + + else if (boxRgt < pt.X && boxRgt > lft) + lft = boxRgt; + } + + if (rgt == double.MaxValue) + { + if (bounds.Right > pt.X) + rgt = bounds.Right; + else return Box.Empty; + } + + if (lft == double.MinValue) + { + if (bounds.Left < pt.X) + lft = bounds.Left; + else return Box.Empty; + } + + #endregion + + return new Box(lft, btm, rgt - lft, top - btm); + } + + public static Box GetLargestBoxHorizontally(Vector pt, Box bounds, IEnumerable boxes) + { + var horizontalBoxes = boxes.Where(b => !(b.Bottom > pt.Y || b.Top < pt.Y)).ToList(); + + #region Find Left/Right Limits + + var lft = double.MinValue; + var rgt = double.MaxValue; + + foreach (var box in horizontalBoxes) + { + var boxLft = box.Left; + var boxRgt = box.Right; + + if (boxLft > pt.X && boxLft < rgt) + rgt = boxLft; + + else if (boxRgt < pt.X && boxRgt > lft) + lft = boxRgt; + } + + if (rgt == double.MaxValue) + { + if (bounds.Right > pt.X) + rgt = bounds.Right; + else return Box.Empty; + } + + if (lft == double.MinValue) + { + if (bounds.Left < pt.X) + lft = bounds.Left; + else return Box.Empty; + } + + #endregion + + var verticalBoxes = boxes.Where(b => !(b.Left >= rgt || b.Right <= lft)).ToList(); + + #region Find Top/Bottom Limits + + var top = double.MaxValue; + var btm = double.MinValue; + + foreach (var box in verticalBoxes) + { + var boxBtm = box.Bottom; + var boxTop = box.Top; + + if (boxBtm > pt.Y && boxBtm < top) + top = boxBtm; + + else if (box.Top < pt.Y && boxTop > btm) + btm = boxTop; + } + + if (top == double.MaxValue) + { + if (bounds.Top > pt.Y) + top = bounds.Top; + else return Box.Empty; + } + + if (btm == double.MinValue) + { + if (bounds.Bottom < pt.Y) + btm = bounds.Bottom; + else return Box.Empty; + } + + #endregion + + return new Box(lft, btm, rgt - lft, top - btm); + } + } +} diff --git a/Source/OpenNest.Core/IBoundable.cs b/Source/OpenNest.Core/IBoundable.cs new file mode 100644 index 0000000..2b95ead --- /dev/null +++ b/Source/OpenNest.Core/IBoundable.cs @@ -0,0 +1,18 @@ + +namespace OpenNest +{ + public interface IBoundable + { + Box BoundingBox { get; } + + double Left { get; } + + double Right { get; } + + double Top { get; } + + double Bottom { get; } + + void UpdateBounds(); + } +} diff --git a/Source/OpenNest.Core/IPostProcessor.cs b/Source/OpenNest.Core/IPostProcessor.cs new file mode 100644 index 0000000..f34a65c --- /dev/null +++ b/Source/OpenNest.Core/IPostProcessor.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace OpenNest +{ + public interface IPostProcessor + { + string Name { get; } + + string Author { get; } + + string Description { get; } + + void Post(Nest nest, Stream outputStream); + + void Post(Nest nest, string outputFile); + } +} diff --git a/Source/OpenNest.Core/Material.cs b/Source/OpenNest.Core/Material.cs new file mode 100644 index 0000000..c033d2b --- /dev/null +++ b/Source/OpenNest.Core/Material.cs @@ -0,0 +1,33 @@ +namespace OpenNest +{ + public class Material + { + public Material() + { + } + + public Material(string name) + { + Name = name; + } + + public Material(string name, string grade) + { + Name = name; + Grade = grade; + } + + public Material(string name, string grade, double density) + { + Name = name; + Grade = grade; + Density = density; + } + + public string Name { get; set; } + + public string Grade { get; set; } + + public double Density { get; set; } + } +} diff --git a/Source/OpenNest.Core/Nest.cs b/Source/OpenNest.Core/Nest.cs new file mode 100644 index 0000000..0b20dce --- /dev/null +++ b/Source/OpenNest.Core/Nest.cs @@ -0,0 +1,139 @@ +using System; +using OpenNest.Collections; + +namespace OpenNest +{ + public class Nest + { + public PlateCollection Plates; + public DrawingCollection Drawings; + + public Nest() + : this(string.Empty) + { + } + + public Nest(string name) + { + Name = name; + Plates = new PlateCollection(); + Plates.PlateRemoved += Plates_PlateRemoved; + Drawings = new DrawingCollection(); + PlateDefaults = new PlateSettings(); + Customer = string.Empty; + Notes = string.Empty; + } + + private static void Plates_PlateRemoved(object sender, PlateRemovedEventArgs e) + { + e.Plate.Parts.Clear(); + } + + public string Name { get; set; } + + public string Customer { get; set; } + + public string Notes { get; set; } + + public Units Units { get; set; } + + public DateTime DateCreated { get; set; } + + public DateTime DateLastModified { get; set; } + + public PlateSettings PlateDefaults { get; set; } + + public Plate CreatePlate() + { + var plate = PlateDefaults.CreateNew(); + Plates.Add(plate); + return plate; + } + + public void UpdateDrawingQuantities() + { + foreach (var drawing in Drawings) + { + drawing.Quantity.Nested = 0; + } + + foreach (var plate in Plates) + { + foreach (var part in plate.Parts) + { + part.BaseDrawing.Quantity.Nested += plate.Quantity; + } + } + } + + public class PlateSettings + { + private readonly Plate plate; + + public PlateSettings() + { + plate = new Plate(); + } + + public int Quadrant + { + get { return plate.Quadrant; } + set { plate.Quadrant = value; } + } + + public double Thickness + { + get { return plate.Thickness; } + set { plate.Thickness = value; } + } + + public Material Material + { + get { return plate.Material; } + set { plate.Material = value; } + } + + public Size Size + { + get { return plate.Size; } + set { plate.Size = value; } + } + + public Spacing EdgeSpacing + { + get { return plate.EdgeSpacing; } + set { plate.EdgeSpacing = value; } + } + + public double PartSpacing + { + get { return plate.PartSpacing; } + set { plate.PartSpacing = value; } + } + + public void SetFromExisting(Plate plate) + { + Thickness = plate.Thickness; + Quadrant = plate.Quadrant; + Material = plate.Material; + Size = plate.Size; + EdgeSpacing = plate.EdgeSpacing; + PartSpacing = plate.PartSpacing; + } + + public Plate CreateNew() + { + return new Plate() + { + Thickness = Thickness, + Size = Size, + EdgeSpacing = EdgeSpacing, + PartSpacing = PartSpacing, + Material = Material, + Quadrant = Quadrant, + Quantity = 1 + }; + } + } + } +} diff --git a/Source/OpenNest.Core/NestConstraints.cs b/Source/OpenNest.Core/NestConstraints.cs new file mode 100644 index 0000000..f35e468 --- /dev/null +++ b/Source/OpenNest.Core/NestConstraints.cs @@ -0,0 +1,46 @@ +using System; + +namespace OpenNest +{ + public class NestConstraints + { + /// + /// The rotation step in radians. + /// + public double StepAngle { get; set; } + + /// + /// The rotation start angle in radians. + /// + public double StartAngle { get; set; } + + /// + /// The rotation end angle in radians. + /// + public double EndAngle { get; set; } + + public bool Allow180Equivalent { get; set; } + + /// + /// Sets the StartAngle and EndAngle to allow 360 degree rotation. + /// + public void AllowAnyRotation() + { + StartAngle = 0; + EndAngle = Angle.TwoPI; + } + + public bool HasLimitedRotation() + { + var diff = EndAngle - StartAngle; + + if (diff.IsEqualTo(Angle.TwoPI)) + return false; + + if ((diff > Math.PI || diff.IsEqualTo(Math.PI)) && Allow180Equivalent) + return false; + + return true; + } + } +} diff --git a/Source/OpenNest.Core/OffsetSide.cs b/Source/OpenNest.Core/OffsetSide.cs new file mode 100644 index 0000000..fb86c1a --- /dev/null +++ b/Source/OpenNest.Core/OffsetSide.cs @@ -0,0 +1,9 @@ + +namespace OpenNest +{ + public enum OffsetSide + { + Left, + Right + } +} diff --git a/Source/OpenNest.Core/OpenNest.Core.csproj b/Source/OpenNest.Core/OpenNest.Core.csproj new file mode 100644 index 0000000..bfe629b --- /dev/null +++ b/Source/OpenNest.Core/OpenNest.Core.csproj @@ -0,0 +1,109 @@ + + + + + Debug + AnyCPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF} + Library + Properties + OpenNest + OpenNest.Core + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/OpenNest.Core/Part.cs b/Source/OpenNest.Core/Part.cs new file mode 100644 index 0000000..2a8ac44 --- /dev/null +++ b/Source/OpenNest.Core/Part.cs @@ -0,0 +1,190 @@ +using System.Collections.Generic; +using OpenNest.CNC; + +namespace OpenNest +{ + public interface IPart : IBoundable + { + Vector Location { get; set; } + double Rotation { get; } + void Rotate(double angle); + void Rotate(double angle, Vector origin); + void Offset(double x, double y); + void Offset(Vector voffset); + void Update(); + } + + public class Part : IPart, IBoundable + { + private Vector location; + + public readonly Drawing BaseDrawing; + + public Part(Drawing baseDrawing) + : this(baseDrawing, new Vector()) + { + } + + public Part(Drawing baseDrawing, Vector location) + { + BaseDrawing = baseDrawing; + Program = baseDrawing.Program.Clone() as Program; + this.location = location; + UpdateBounds(); + } + + /// + /// Location of the part. + /// + public Vector Location + { + get { return location; } + set + { + BoundingBox.Offset(value - location); + location = value; + } + } + + public Program Program { get; private set; } + + /// + /// Gets the rotation of the part in radians. + /// + public double Rotation + { + get { return Program.Rotation; } + } + + /// + /// Rotates the part. + /// + /// Angle of rotation in radians. + public void Rotate(double angle) + { + Program.Rotate(angle); + location = Location.Rotate(angle); + UpdateBounds(); + } + + /// + /// Rotates the part around the specified origin. + /// + /// Angle of rotation in radians. + /// The origin to rotate the part around. + public void Rotate(double angle, Vector origin) + { + Program.Rotate(angle); + location = Location.Rotate(angle, origin); + UpdateBounds(); + } + + /// + /// Offsets the part. + /// + /// The x-axis offset distance. + /// The y-axis offset distance. + public void Offset(double x, double y) + { + location = new Vector(location.X + x, location.Y + y); + BoundingBox.Offset(x, y); + } + + /// + /// Offsets the part. + /// + /// The vector containing the x-axis & y-axis offset distances. + public void Offset(Vector voffset) + { + location += voffset; + BoundingBox.Offset(voffset); + } + + /// + /// Updates the bounding box of the part. + /// + public void UpdateBounds() + { + BoundingBox = Program.BoundingBox(); + BoundingBox.Offset(Location); + } + + /// + /// Updates the part from the drawing it was derived from. + /// + public void Update() + { + var rotation = Rotation; + Program = BaseDrawing.Program.Clone() as Program; + Program.Rotate(Program.Rotation - rotation); + } + + /// + /// The smallest box that contains the part. + /// + public Box BoundingBox { get; protected set; } + + public bool Intersects(Part part, out List pts) + { + pts = new List(); + + var entities1 = ConvertProgram.ToGeometry(Program); + var entities2 = ConvertProgram.ToGeometry(part.Program); + + var shapes1 = Helper.GetShapes(entities1); + var shapes2 = Helper.GetShapes(entities2); + + shapes1.ForEach(shape => shape.Offset(Location)); + shapes2.ForEach(shape => shape.Offset(part.Location)); + + for (int i = 0; i < shapes1.Count; i++) + { + var shape1 = shapes1[i]; + + for (int j = 0; j < shapes2.Count; j++) + { + var shape2 = shapes2[j]; + List pts2; + + if (shape1.Intersects(shape2, out pts2)) + pts.AddRange(pts2); + } + } + + return pts.Count > 0; + } + + public double Left + { + get { return BoundingBox.Left; } + } + + public double Right + { + get { return BoundingBox.Right; } + } + + public double Top + { + get { return BoundingBox.Top; } + } + + public double Bottom + { + get { return BoundingBox.Bottom; } + } + + /// + /// Gets a deep copy of the part. + /// + /// + public object Clone() + { + var part = new Part(BaseDrawing); + part.Rotate(Rotation); + part.Location = Location; + + return part; + } + } +} diff --git a/Source/OpenNest.Core/Plate.cs b/Source/OpenNest.Core/Plate.cs new file mode 100644 index 0000000..8562fea --- /dev/null +++ b/Source/OpenNest.Core/Plate.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenNest.Collections; + +namespace OpenNest +{ + public class Plate + { + private int quadrant; + + public event EventHandler PartAdded + { + add { Parts.PartAdded += value; } + remove { Parts.PartAdded -= value; } + } + + public event EventHandler PartRemoved + { + add { Parts.PartRemoved += value; } + remove { Parts.PartRemoved -= value; } + } + + public event EventHandler PartChanged + { + add { Parts.PartChanged += value; } + remove { Parts.PartChanged -= value; } + } + + public Plate() + : this(60, 120) + { + } + + public Plate(double width, double length) + : this(new Size(length, width)) + { + } + + public Plate(Size size) + { + EdgeSpacing = new Spacing(); + Size = size; + Material = new Material(); + Parts = new PartCollection(); + Parts.PartAdded += Parts_PartAdded; + Parts.PartRemoved += Parts_PartRemoved; + Quadrant = 1; + } + + private void Parts_PartAdded(object sender, PartAddedEventArgs e) + { + e.Part.BaseDrawing.Quantity.Nested += Quantity; + } + + private void Parts_PartRemoved(object sender, PartRemovedEventArgs e) + { + e.Part.BaseDrawing.Quantity.Nested -= Quantity; + } + + /// + /// Thickness of the plate. + /// + public double Thickness { get; set; } + + /// + /// The spacing between parts. + /// + public double PartSpacing { get; set; } + + /// + /// The spacing along the edges of the plate. + /// + public Spacing EdgeSpacing; + + /// + /// The size of the plate. + /// + public Size Size { get; set; } + + /// + /// Material the plate is made out of. + /// + public Material Material { get; set; } + + /// + /// The parts that the plate contains. + /// + public PartCollection Parts { get; set; } + + /// + /// The number of times to cut the plate. + /// + public int Quantity { get; set; } + + /// + /// The quadrant the plate is located in. + /// 1 = TopRight + /// 2 = TopLeft + /// 3 = BottomLeft + /// 4 = BottomRight + /// + public int Quadrant + { + get { return quadrant; } + set { quadrant = value <= 4 && value > 0 ? value : 1; } + } + + /// + /// Rotates the plate clockwise or counter-clockwise along with all parts. + /// + /// + /// + public void Rotate90(RotationType rotationDirection, bool keepSameQuadrant = true) + { + const double oneAndHalfPI = Math.PI * 1.5; + + Size = new Size(Size.Height, Size.Width); + + if (rotationDirection == RotationType.CW) + { + Rotate(oneAndHalfPI); + + if (keepSameQuadrant) + { + switch (Quadrant) + { + case 1: + Offset(0, Size.Height); + break; + + case 2: + Offset(-Size.Width, 0); + break; + + case 3: + Offset(0, -Size.Height); + break; + + case 4: + Offset(Size.Width, 0); + break; + + default: + return; + } + } + else + { + Quadrant = Quadrant < 2 ? 4 : Quadrant - 1; + } + } + else + { + Rotate(Angle.HalfPI); + + if (keepSameQuadrant) + { + switch (Quadrant) + { + case 1: + Offset(Size.Width, 0); + break; + + case 2: + Offset(0, Size.Height); + break; + + case 3: + Offset(-Size.Width, 0); + break; + + case 4: + Offset(0, -Size.Height); + break; + + default: + return; + } + } + else + { + Quadrant = Quadrant > 3 ? 1 : Quadrant + 1; + } + } + } + + /// + /// Rotates the plate 180 degrees along with all parts. + /// + /// + public void Rotate180(bool keepSameQuadrant = true) + { + if (keepSameQuadrant) + { + Vector centerpt; + + switch (Quadrant) + { + case 1: + centerpt = new Vector(Size.Width * 0.5, Size.Height * 0.5); + break; + + case 2: + centerpt = new Vector(-Size.Width * 0.5, Size.Height * 0.5); + break; + + case 3: + centerpt = new Vector(-Size.Width * 0.5, -Size.Height * 0.5); + break; + + case 4: + centerpt = new Vector(Size.Width * 0.5, -Size.Height * 0.5); + break; + + default: + return; + } + + Rotate(Math.PI, centerpt); + } + else + { + Rotate(Math.PI); + Quadrant = (Quadrant + 2) % 4; + + if (Quadrant == 0) + Quadrant = 4; + } + } + + /// + /// Rotates the parts on the plate. + /// + /// + public void Rotate(double angle) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Rotate(angle); + } + } + + /// + /// Rotates the parts on the plate around the specified origin. + /// + /// + /// + public void Rotate(double angle, Vector origin) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Rotate(angle, origin); + } + } + + /// + /// Offsets the parts on the plate. + /// + /// + /// + public void Offset(double x, double y) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Offset(x, y); + } + } + + /// + /// Offsets the parts on the plate. + /// + /// + public void Offset(Vector voffset) + { + for (int i = 0; i < Parts.Count; ++i) + { + var part = Parts[i]; + part.Offset(voffset); + } + } + + /// + /// The smallest box that contains the plate. + /// + /// + /// + public Box BoundingBox(bool includeParts = true) + { + var plateBox = new Box(); + + switch (Quadrant) + { + case 1: + plateBox.X = 0; + plateBox.Y = 0; + break; + + case 2: + plateBox.X = (float)-Size.Width; + plateBox.Y = 0; + break; + + case 3: + plateBox.X = (float)-Size.Width; + plateBox.Y = (float)-Size.Height; + break; + + case 4: + plateBox.X = 0; + plateBox.Y = (float)-Size.Height; + break; + + default: + return new Box(); + } + + plateBox.Width = Size.Width; + plateBox.Height = Size.Height; + + if (!includeParts) + return plateBox; + + var boundingBox = new Box(); + var partsBox = Parts.GetBoundingBox(); + + boundingBox.X = partsBox.Left < plateBox.Left + ? partsBox.Left + : plateBox.Left; + + boundingBox.Y = partsBox.Bottom < plateBox.Bottom + ? partsBox.Bottom + : plateBox.Bottom; + + boundingBox.Width = partsBox.Right > plateBox.Right + ? partsBox.Right - boundingBox.X + : plateBox.Right - boundingBox.X; + + boundingBox.Height = partsBox.Top > plateBox.Top + ? partsBox.Top - boundingBox.Y + : plateBox.Top - boundingBox.Y; + + return boundingBox; + } + + /// + /// The area within the edge spacing. + /// + /// + public Box WorkArea() + { + var box = BoundingBox(false); + + box.X += EdgeSpacing.Left; + box.Y += EdgeSpacing.Bottom; + box.Width -= EdgeSpacing.Left + EdgeSpacing.Right; + box.Height -= EdgeSpacing.Top + EdgeSpacing.Bottom; + + return box; + } + + /// + /// Automatically sizes the plate to fit the parts. + /// + /// The factor to round the actual size up to. + /// + /// AutoSize 9.7 x 10.1 + /// * roundingFactor=1.0 new Size=10 x 11 + /// * roundingFactor=0.5 new Size=10 x 10.5 + /// * roundingFactor=0.25 new Size=9.75 x 10.25 + /// * roundingFactor=0.0 new Size=9.7 x 10.1 + /// + public void AutoSize(double roundingFactor = 1.0) + { + if (Parts.Count == 0) + return; + + var bounds = Parts.GetBoundingBox(); + + double width; + double height; + + switch (Quadrant) + { + case 1: + width = Math.Abs(bounds.Right) + EdgeSpacing.Right; + height = Math.Abs(bounds.Top) + EdgeSpacing.Top; + break; + + case 2: + width = Math.Abs(bounds.Left) + EdgeSpacing.Left; + height = Math.Abs(bounds.Top) + EdgeSpacing.Top; + break; + + case 3: + width = Math.Abs(bounds.Left) + EdgeSpacing.Left; + height = Math.Abs(bounds.Bottom) + EdgeSpacing.Bottom; + break; + + case 4: + width = Math.Abs(bounds.Right) + EdgeSpacing.Right; + height = Math.Abs(bounds.Bottom) + EdgeSpacing.Bottom; + break; + + default: + return; + } + + Size = new Size( + Helper.RoundUpToNearest(width, roundingFactor), + Helper.RoundUpToNearest(height, roundingFactor)); + } + + /// + /// Gets the area of the top surface of the plate. + /// + /// + public double Area() + { + return Size.Width * Size.Height; + } + + /// + /// Gets the volume of the plate. + /// + /// + public double Volume() + { + return Area() * Thickness; + } + + /// + /// Gets the weight of the plate. + /// + /// + public double Weight() + { + return Volume() * Material.Density; + } + + /// + /// Percentage of the material used. + /// + /// Returns a number between 0.0 and 1.0 + public double Utilization() + { + return Parts.Sum(part => part.BaseDrawing.Area) / Area(); + } + + public bool HasOverlappingParts(out List pts) + { + pts = new List(); + + for (int i = 0; i < Parts.Count; i++) + { + var part1 = Parts[i]; + + for (int j = i + 1; j < Parts.Count; j++) + { + var part2 = Parts[j]; + + List pts2; + + if (part1.Intersects(part2, out pts2)) + pts.AddRange(pts2); + } + } + + return pts.Count > 0; + } + } +} diff --git a/Source/OpenNest.Core/Properties/AssemblyInfo.cs b/Source/OpenNest.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bd5d103 --- /dev/null +++ b/Source/OpenNest.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenNest.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenNest.Core")] +[assembly: AssemblyCopyright("Copyright © AJ Isaacs 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("13d3c141-e430-4f27-9098-60d6f93e2a7b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.4.0.0")] +[assembly: AssemblyFileVersion("0.4.0.0")] diff --git a/Source/OpenNest.Core/Quantity.cs b/Source/OpenNest.Core/Quantity.cs new file mode 100644 index 0000000..2a022a4 --- /dev/null +++ b/Source/OpenNest.Core/Quantity.cs @@ -0,0 +1,14 @@ +namespace OpenNest +{ + public struct Quantity + { + public int Nested { get; internal set; } + + public int Required { get; set; } + + public int Remaining + { + get { return Required - Nested; } + } + } +} diff --git a/Source/OpenNest.Core/RelativePosition.cs b/Source/OpenNest.Core/RelativePosition.cs new file mode 100644 index 0000000..7982cda --- /dev/null +++ b/Source/OpenNest.Core/RelativePosition.cs @@ -0,0 +1,13 @@ + +namespace OpenNest +{ + public enum RelativePosition + { + Intersecting, + Left, + Right, + Top, + Bottom, + None + } +} diff --git a/Source/OpenNest.Core/RotationType.cs b/Source/OpenNest.Core/RotationType.cs new file mode 100644 index 0000000..03c106f --- /dev/null +++ b/Source/OpenNest.Core/RotationType.cs @@ -0,0 +1,16 @@ + +namespace OpenNest +{ + public enum RotationType + { + /// + /// Clockwise + /// + CW, + + /// + /// Counter-Clockwise + /// + CCW + } +} diff --git a/Source/OpenNest.Core/Sequence.cs b/Source/OpenNest.Core/Sequence.cs new file mode 100644 index 0000000..e1f1833 --- /dev/null +++ b/Source/OpenNest.Core/Sequence.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace OpenNest +{ + public interface ISequencer + { + List SequenceParts(IList parts); + List SequenceParts(IList parts, Vector origin); + } + + public class SequenceByNearest : ISequencer + { + public List SequenceParts(IList parts) + { + return SequenceParts(parts, Vector.Zero); + } + + public List SequenceParts(IList parts, Vector origin) + { + if (parts.Count == 0) + return new List(); + + var dupList = new List(parts); + var seqList = new List(parts.Count); + + var lastPart = GetClosestPart(origin, dupList); + seqList.Add(lastPart); + dupList.Remove(lastPart); + + for (int i = 0; i < parts.Count - 1 /*STOP BEFORE LAST PART*/; i++) + { + var nextPart = GetClosestPart(lastPart.Location, dupList); + + if (nextPart == null) + break; + + seqList.Add(nextPart); + dupList.Remove(nextPart); + lastPart = nextPart; + } + + return seqList; + } + + private Part GetClosestPart(Vector pt, IList parts) + { + if (parts.Count == 0) + return null; + + var closestPart = parts[0]; + var closestDistance = parts[0].Location.DistanceTo(pt); + + for (int i = 1; i < parts.Count; i++) + { + var distance = parts[i].Location.DistanceTo(pt); + + if (distance < closestDistance) + { + closestPart = parts[i]; + closestDistance = distance; + } + } + + return closestPart; + } + } +} diff --git a/Source/OpenNest.Core/Size.cs b/Source/OpenNest.Core/Size.cs new file mode 100644 index 0000000..1fb89c2 --- /dev/null +++ b/Source/OpenNest.Core/Size.cs @@ -0,0 +1,55 @@ +using System; + +namespace OpenNest +{ + public struct Size + { + public Size(double width, double height) + { + Height = height; + Width = width; + } + + public double Height; + + public double Width; + + public static Size Parse(string size) + { + var a = size.ToUpper().Split('X'); + + if (a.Length > 2) + throw new FormatException("Invalid size format."); + + var height = double.Parse(a[0]); + var width = double.Parse(a[1]); + + return new Size(width, height); + } + + public static bool TryParse(string s, out Size size) + { + try + { + size = Parse(s); + } + catch + { + size = new Size(0, 0); + return false; + } + + return true; + } + + public override string ToString() + { + return string.Format("{0} x {1}", Height, Width); + } + + public string ToString(int decimalPlaces) + { + return string.Format("{0} x {1}", Math.Round(Height, decimalPlaces), Math.Round(Width, decimalPlaces)); + } + } +} diff --git a/Source/OpenNest.Core/Spacing.cs b/Source/OpenNest.Core/Spacing.cs new file mode 100644 index 0000000..918fa7b --- /dev/null +++ b/Source/OpenNest.Core/Spacing.cs @@ -0,0 +1,27 @@ +namespace OpenNest +{ + public struct Spacing + { + public Spacing(double topBottom, double leftRight) + { + Top = Bottom = topBottom; + Left = Right = leftRight; + } + + public Spacing(double left, double bottom, double right, double top) + { + Left = left; + Bottom = bottom; + Right = right; + Top = top; + } + + public double Left; + + public double Bottom; + + public double Right; + + public double Top; + } +} diff --git a/Source/OpenNest.Core/SpecialLayers.cs b/Source/OpenNest.Core/SpecialLayers.cs new file mode 100644 index 0000000..9931fae --- /dev/null +++ b/Source/OpenNest.Core/SpecialLayers.cs @@ -0,0 +1,21 @@ +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class SpecialLayers + { + public static readonly Layer Default = new Layer("0"); + + public static readonly Layer Cut = new Layer("CUT"); + + public static readonly Layer Rapid = new Layer("RAPID"); + + public static readonly Layer Display = new Layer("DISPLAY"); + + public static readonly Layer Leadin = new Layer("LEADIN"); + + public static readonly Layer Leadout = new Layer("LEADOUT"); + + public static readonly Layer Scribe = new Layer("SCRIBE"); + } +} diff --git a/Source/OpenNest.Core/Timing.cs b/Source/OpenNest.Core/Timing.cs new file mode 100644 index 0000000..9ab5565 --- /dev/null +++ b/Source/OpenNest.Core/Timing.cs @@ -0,0 +1,91 @@ +using System; +using System.Linq; +using OpenNest.CNC; +using OpenNest.Geometry; + +namespace OpenNest +{ + public static class Timing + { + public static TimingInfo GetTimingInfo(Program pgm) + { + var entities = ConvertProgram.ToGeometry(pgm); + var shapes = Helper.GetShapes(entities.Where(entity => entity.Layer != SpecialLayers.Rapid)); + var info = new TimingInfo { PierceCount = shapes.Count }; + + var last = entities[0]; + ProcessEntity(info, last, null); + + for (int i = 1; i < entities.Count; i++) + { + var entity = entities[i]; + ProcessEntity(info, entity, last); + last = entity; + } + + return info; + } + + public static TimingInfo GetTimingInfo(Plate plate) + { + var info = new TimingInfo(); + var pos = new Vector(0, 0); + + foreach (var part in plate.Parts) + { + var endpt = part.Program.EndPoint() + part.Location; + info += GetTimingInfo(part.Program); + info.TravelDistance += pos.DistanceTo(endpt); + pos = endpt; + } + + info *= plate.Quantity; + + return info; + } + + public static TimingInfo GetTimingInfo(Nest nest) + { + var info = new TimingInfo(); + return nest.Plates.Aggregate(info, (current, plate) => current + GetTimingInfo(plate)); + } + + private static void ProcessEntity(TimingInfo info, Entity entity, Entity lastEntity) + { + if (entity.Layer == SpecialLayers.Cut) + { + info.CutDistance += entity.Length; + + if (entity.Type == EntityType.Line && + lastEntity != null && + lastEntity.Type == EntityType.Line && + lastEntity.Layer == SpecialLayers.Cut) + info.IntersectionCount++; + } + else if (entity.Layer == SpecialLayers.Rapid) + info.TravelDistance += entity.Length; + } + + public static TimeSpan CalculateTime(TimingInfo info, CutParameters cutParams) + { + var time = new TimeSpan(); + + switch (cutParams.Units) + { + case Units.Inches: + time += TimeSpan.FromMinutes(info.CutDistance / cutParams.Feedrate); + time += TimeSpan.FromMinutes(info.TravelDistance / cutParams.RapidTravelRate); + break; + + case Units.Millimeters: + time += TimeSpan.FromSeconds(info.CutDistance / cutParams.Feedrate); + time += TimeSpan.FromSeconds(info.TravelDistance / cutParams.RapidTravelRate); + break; + } + + time += TimeSpan.FromTicks(info.PierceCount * cutParams.PierceTime.Ticks); + + return time; + } + } +} diff --git a/Source/OpenNest.Core/TimingInfo.cs b/Source/OpenNest.Core/TimingInfo.cs new file mode 100644 index 0000000..e0f5854 --- /dev/null +++ b/Source/OpenNest.Core/TimingInfo.cs @@ -0,0 +1,57 @@ +namespace OpenNest +{ + public class TimingInfo + { + public int PierceCount; + + public int IntersectionCount; + + public double TravelDistance; + + public double CutDistance; + + public static TimingInfo operator +(TimingInfo info1, TimingInfo info2) + { + return new TimingInfo + { + CutDistance = info1.CutDistance + info2.CutDistance, + IntersectionCount = info1.IntersectionCount + info2.IntersectionCount, + TravelDistance = info1.TravelDistance + info2.TravelDistance, + PierceCount = info1.PierceCount + info2.PierceCount + }; + } + + public static TimingInfo operator -(TimingInfo info1, TimingInfo info2) + { + return new TimingInfo + { + CutDistance = info1.CutDistance - info2.CutDistance, + IntersectionCount = info1.IntersectionCount - info2.IntersectionCount, + TravelDistance = info1.TravelDistance - info2.TravelDistance, + PierceCount = info1.PierceCount - info2.PierceCount + }; + } + + public static TimingInfo operator *(TimingInfo info1, TimingInfo info2) + { + return new TimingInfo + { + CutDistance = info1.CutDistance * info2.CutDistance, + IntersectionCount = info1.IntersectionCount * info2.IntersectionCount, + TravelDistance = info1.TravelDistance * info2.TravelDistance, + PierceCount = info1.PierceCount * info2.PierceCount + }; + } + + public static TimingInfo operator *(TimingInfo info1, int factor) + { + return new TimingInfo + { + CutDistance = info1.CutDistance * factor, + IntersectionCount = info1.IntersectionCount * factor, + TravelDistance = info1.TravelDistance * factor, + PierceCount = info1.PierceCount * factor + }; + } + } +} diff --git a/Source/OpenNest.Core/Tolerance.cs b/Source/OpenNest.Core/Tolerance.cs new file mode 100644 index 0000000..15924b0 --- /dev/null +++ b/Source/OpenNest.Core/Tolerance.cs @@ -0,0 +1,14 @@ +using System; + +namespace OpenNest +{ + public static class Tolerance + { + public const double Epsilon = 0.00001; + + public static bool IsEqualTo(this double a, double b, double tolerance = Epsilon) + { + return Math.Abs(b - a) <= tolerance; + } + } +} diff --git a/Source/OpenNest.Core/Trigonometry.cs b/Source/OpenNest.Core/Trigonometry.cs new file mode 100644 index 0000000..b3c3778 --- /dev/null +++ b/Source/OpenNest.Core/Trigonometry.cs @@ -0,0 +1,40 @@ +using System; + +namespace OpenNest +{ + public static class Trigonometry + { + /// + /// + /// + /// Height + /// Hypotenuse + /// + public static double Base(double height, double hypotenuse) + { + return Math.Sqrt(hypotenuse * hypotenuse - height * height); + } + + /// + /// + /// + /// Base + /// Hypotenuse + /// + public static double Height(double bottom, double hypotenuse) + { + return Math.Sqrt(hypotenuse * hypotenuse - bottom * bottom); + } + + /// + /// + /// + /// Height + /// Base + /// + public static double Hypotenuse(double height, double bottom) + { + return Math.Sqrt(height * height + bottom * bottom); + } + } +} diff --git a/Source/OpenNest.Core/Units.cs b/Source/OpenNest.Core/Units.cs new file mode 100644 index 0000000..3b538b4 --- /dev/null +++ b/Source/OpenNest.Core/Units.cs @@ -0,0 +1,82 @@ + +namespace OpenNest +{ + public enum Units + { + Inches, + Millimeters + } + + public static class UnitsHelper + { + public static string GetShortString(Units units) + { + switch (units) + { + case Units.Inches: + return "in"; + + case Units.Millimeters: + return "mm"; + + default: + return string.Empty; + } + } + + public static string GetLongString(Units units) + { + switch (units) + { + case Units.Inches: + return "inches"; + + case Units.Millimeters: + return "millimeters"; + + default: + return string.Empty; + } + } + + public static string GetShortTimeUnit(Units units) + { + switch (units) + { + case Units.Inches: + return "min"; + + case Units.Millimeters: + return "sec"; + + default: + return string.Empty; + } + } + + public static string GetLongTimeUnit(Units units) + { + switch (units) + { + case Units.Inches: + return "minute"; + + case Units.Millimeters: + return "second"; + + default: + return string.Empty; + } + } + + public static string GetShortTimeUnitPair(Units units) + { + return GetShortString(units) + "/" + GetShortTimeUnit(units); + } + + public static string GetLongTimeUnitPair(Units units) + { + return GetLongString(units) + "/" + GetLongTimeUnit(units); + } + } +} diff --git a/Source/OpenNest.Core/Vector.cs b/Source/OpenNest.Core/Vector.cs new file mode 100644 index 0000000..7252df1 --- /dev/null +++ b/Source/OpenNest.Core/Vector.cs @@ -0,0 +1,213 @@ +using System; + +namespace OpenNest +{ + public struct Vector + { + public static readonly Vector Invalid = new Vector(double.NaN, double.NaN); + public static readonly Vector Zero = new Vector(0, 0); + + public double X; + public double Y; + + public Vector(double x, double y) + { + X = x; + Y = y; + } + + public double DistanceTo(Vector pt) + { + var vx = pt.X - X; + var vy = pt.Y - Y; + + return Math.Sqrt(vx * vx + vy * vy); + } + + public double DistanceTo(double x, double y) + { + var vx = x - X; + var vy = y - Y; + + return Math.Sqrt(vx * vx + vy * vy); + } + + public double DotProduct(Vector pt) + { + return X * pt.X + Y * pt.Y; + } + + public double Angle() + { + return OpenNest.Angle.NormalizeRad(Math.Atan2(Y, X)); + } + + /// + /// Returns the angle to the given point when the origin is this point. + /// + /// + /// + public double AngleTo(Vector pt) + { + return (pt - this).Angle(); + } + + /// + /// Returns the angle when the origin is set at the given point. + /// + /// + /// + public double AngleFrom(Vector pt) + { + return (this - pt).Angle(); + } + + /// + /// Returns the angle between this point and the given point. + /// Source: http://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors + /// + /// + /// + public double AngleBetween(Vector pt) + { + var v1 = Normalize(); + var v2 = pt.Normalize(); + var dot = v1.X * v2.X + v1.Y + v2.Y; + var det = v1.X * v2.X - v1.Y + v2.Y; + + return Math.Atan2(det, dot); + } + + public static Vector operator +(Vector pt1, Vector pt2) + { + return new Vector(pt1.X + pt2.X, pt1.Y + pt2.Y); + } + + public static Vector operator -(Vector pt1, Vector pt2) + { + return new Vector(pt1.X - pt2.X, pt1.Y - pt2.Y); + } + + public static Vector operator -(Vector pt) + { + return new Vector(-pt.X, -pt.Y); + } + + public static Vector operator *(Vector pt, double factor) + { + return new Vector(pt.X * factor, pt.Y * factor); + } + + public static Vector operator *(double factor, Vector pt) + { + return new Vector(pt.X * factor, pt.Y * factor); + } + + public static Vector operator *(Vector pt, Vector factor) + { + return new Vector(pt.X * factor.X, pt.Y * factor.Y); + } + + public static Vector operator /(Vector pt, double divisor) + { + return new Vector(pt.X / divisor, pt.Y / divisor); + } + + public static bool operator ==(Vector pt1, Vector pt2) + { + return pt1.X.IsEqualTo(pt2.X) && pt1.Y.IsEqualTo(pt2.Y); + } + + public static bool operator !=(Vector pt1, Vector pt2) + { + return !(pt1 == pt2); + } + + /// + /// Returns the unit vector equivalent to this point. + /// + /// + public Vector Normalize() + { + var d = DistanceTo(Vector.Zero); + return new Vector(X / d, Y / d); + } + + public Vector Rotate(double angle) + { + var v = new Vector(); + + var cos = Math.Cos(angle); + var sin = Math.Sin(angle); + + v.X = X * cos - Y * sin; + v.Y = X * sin + Y * cos; + + return v; + } + + public Vector Rotate(double angle, Vector origin) + { + var v = new Vector(); + var pt = this - origin; + + var cos = Math.Cos(angle); + var sin = Math.Sin(angle); + + v.X = pt.X * cos - pt.Y * sin + origin.X; + v.Y = pt.X * sin + pt.Y * cos + origin.Y; + + return v; + } + + public Vector Offset(double x, double y) + { + return new Vector(X + x, Y + y); + } + + public Vector Offset(Vector voffset) + { + return this + voffset; + } + + public Vector Scale(double factor) + { + return new Vector(X * factor, Y * factor); + } + + public Vector Scale(double factor, Vector origin) + { + return (this - origin) * factor + origin; + } + + public Vector Clone() + { + return new Vector(X, Y); + } + + public override bool Equals(object obj) + { + if (!(obj is Vector)) + return false; + + var pt = (Vector)obj; + + return (X.IsEqualTo(pt.X)) && (Y.IsEqualTo(pt.Y)); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return string.Format("[Vector: X:{0}, Y:{1}]", X, Y); + } + + public bool IsValid() + { + return !double.IsNaN(X) && !double.IsNaN(Y); + } + } +} diff --git a/Source/OpenNest.Engine/BestCombination.cs b/Source/OpenNest.Engine/BestCombination.cs new file mode 100644 index 0000000..84260cb --- /dev/null +++ b/Source/OpenNest.Engine/BestCombination.cs @@ -0,0 +1,80 @@ +using System; + +namespace OpenNest +{ + internal static class BestCombination + { + public static bool FindFrom2(double length1, double length2, double overallLength, out int count1, out int count2) + { + overallLength += Tolerance.Epsilon; + + if (length1 > overallLength) + { + if (length2 > overallLength) + { + count1 = 0; + count2 = 0; + return false; + } + + count1 = 0; + count2 = (int)Math.Floor(overallLength / length2); + return true; + } + + if (length2 > overallLength) + { + count1 = (int)Math.Floor(overallLength / length1); + count2 = 0; + return true; + } + + var maxCountLength1 = (int)Math.Floor(overallLength / length1); + + count1 = maxCountLength1; + count2 = 0; + + var remnant = overallLength - maxCountLength1 * length1; + + if (remnant.IsEqualTo(0)) + return true; + + for (int countLength1 = 0; countLength1 <= maxCountLength1; ++countLength1) + { + var remnant1 = overallLength - countLength1 * length1; + + if (remnant1 >= length2) + { + var countLength2 = (int)Math.Floor(remnant1 / length2); + var remnant2 = remnant1 - length2 * countLength2; + + if (!(remnant2 < remnant)) + continue; + + count1 = countLength1; + count2 = countLength2; + + if (remnant2.IsEqualTo(0)) + break; + + remnant = remnant2; + } + else + { + if (!(remnant1 < remnant)) + continue; + + count1 = countLength1; + count2 = 0; + + if (remnant1.IsEqualTo(0)) + break; + + remnant = remnant1; + } + } + + return true; + } + } +} diff --git a/Source/OpenNest.Engine/CirclePacking/Bin.cs b/Source/OpenNest.Engine/CirclePacking/Bin.cs new file mode 100644 index 0000000..742be22 --- /dev/null +++ b/Source/OpenNest.Engine/CirclePacking/Bin.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.CirclePacking +{ + internal class Bin : Box + { + public Bin() + { + Items = new List(); + } + + public List Items { get; set; } + + public double Density() + { + return Items.Sum(i => i.Area()) / Area(); + } + + public object Clone() + { + return new Bin + { + Location = this.Location, + Size = this.Size, + Items = new List(Items) + }; + } + } +} diff --git a/Source/OpenNest.Engine/CirclePacking/FillEndEven.cs b/Source/OpenNest.Engine/CirclePacking/FillEndEven.cs new file mode 100644 index 0000000..f4eac96 --- /dev/null +++ b/Source/OpenNest.Engine/CirclePacking/FillEndEven.cs @@ -0,0 +1,79 @@ +using System; + +namespace OpenNest.CirclePacking +{ + internal class FillEndEven : FillEngine + { + public FillEndEven(Bin bin) + : base(bin) + { + } + + public override void Fill(Item item) + { + var max = new Vector( + Bin.Right - item.BoundingBox.Right + Tolerance.Epsilon, + Bin.Top - item.BoundingBox.Top + Tolerance.Epsilon); + + var rows = Math.Floor((Bin.Height + Tolerance.Epsilon) / (item.Diameter)); + + var diameter = item.Diameter; + var remaining = Bin.Height - diameter * rows; + var radius = diameter * 0.5; + + if (remaining < radius) + { + var yodd = Bin.Y + remaining; + var yoffset = diameter; + var xoffset = Trigonometry.Base(remaining, diameter); + int column = 0; + + for (var x = Bin.X; x <= max.X; x += xoffset) + { + var y = column.IsOdd() ? yodd : Bin.Y; + + for (; y <= max.Y; y += yoffset) + { + Bin.Items.Add(new Item + { + Center = new Vector(x, y) + }); + } + + column++; + } + } + else + { + var yoffset = (Bin.Height - diameter) / (2 * rows - 1); + var xoffset = Trigonometry.Base(yoffset, diameter); + + var yodd = Bin.Y + yoffset; + + yoffset *= 2.0; + + int column = 0; + + for (var x = Bin.X; x <= max.X; x += xoffset) + { + var y = column.IsOdd() ? yodd : Bin.Y; + + for (; y <= max.Y; y += yoffset) + { + Bin.Items.Add(new Item + { + Center = new Vector(x, y) + }); + } + + column++; + } + } + } + + public override void Fill(Item item, int maxCount) + { + throw new NotImplementedException(); + } + } +} diff --git a/Source/OpenNest.Engine/CirclePacking/FillEndOdd.cs b/Source/OpenNest.Engine/CirclePacking/FillEndOdd.cs new file mode 100644 index 0000000..e661e83 --- /dev/null +++ b/Source/OpenNest.Engine/CirclePacking/FillEndOdd.cs @@ -0,0 +1,100 @@ +using System; + +namespace OpenNest.CirclePacking +{ + internal class FillEndOdd : FillEngine + { + public FillEndOdd(Bin bin) + : base(bin) + { + } + + public override void Fill(Item item) + { + var bin1 = FillHorizontal(item); + var bin2 = FillVertical(item); + + if (bin1.Items.Count > bin2.Items.Count) + Bin.Items.AddRange(bin1.Items); + else + Bin.Items.AddRange(bin2.Items); + } + + public override void Fill(Item item, int maxCount) + { + throw new NotImplementedException(); + } + + private Bin FillHorizontal(Item item) + { + var bin = Bin.Clone() as Bin; + + var max = new Vector( + bin.Right - item.BoundingBox.Right + Tolerance.Epsilon, + bin.Top - item.BoundingBox.Top + Tolerance.Epsilon); + + var count = Math.Floor((bin.Width + Tolerance.Epsilon) / item.Diameter); + + if (count == 0) + return bin; + + var xoffset = (bin.Width - item.Diameter) / (count - 1); + var yoffset = Trigonometry.Height(xoffset * 0.5, item.Diameter); + + int row = 0; + + for (var y = bin.Y; y <= max.Y; y += yoffset) + { + var x = row.IsOdd() ? bin.X + xoffset * 0.5 : bin.X; + + for (; x <= max.X; x += xoffset) + { + var addedItem = item.Clone() as Item; + addedItem.Center = new Vector(x, y); + + bin.Items.Add(addedItem); + } + + row++; + } + + return bin; + } + + private Bin FillVertical(Item item) + { + var bin = Bin.Clone() as Bin; + + var max = new Vector( + Bin.Right - item.BoundingBox.Right + Tolerance.Epsilon, + Bin.Top - item.BoundingBox.Top + Tolerance.Epsilon); + + var count = Math.Floor((bin.Height + Tolerance.Epsilon) / item.Diameter); + + if (count == 0) + return bin; + + var yoffset = (bin.Height - item.Diameter) / (count - 1); + var xoffset = Trigonometry.Base(yoffset * 0.5, item.Diameter); + + int column = 0; + + for (var x = bin.X; x <= max.X; x += xoffset) + { + var y = column.IsOdd() ? bin.Y + yoffset * 0.5 : bin.Y; + + for (; y <= max.Y; y += yoffset) + { + var addedItem = item.Clone() as Item; + addedItem.Center = new Vector(x, y); + + bin.Items.Add(addedItem); + } + + column++; + } + + return bin; + } + } +} diff --git a/Source/OpenNest.Engine/CirclePacking/FillEngine.cs b/Source/OpenNest.Engine/CirclePacking/FillEngine.cs new file mode 100644 index 0000000..7fd9488 --- /dev/null +++ b/Source/OpenNest.Engine/CirclePacking/FillEngine.cs @@ -0,0 +1,17 @@ + +namespace OpenNest.CirclePacking +{ + internal abstract class FillEngine + { + public FillEngine(Bin bin) + { + Bin = bin; + } + + public Bin Bin { get; set; } + + public abstract void Fill(Item item); + + public abstract void Fill(Item item, int maxCount); + } +} diff --git a/Source/OpenNest.Engine/CirclePacking/Item.cs b/Source/OpenNest.Engine/CirclePacking/Item.cs new file mode 100644 index 0000000..4b3ad59 --- /dev/null +++ b/Source/OpenNest.Engine/CirclePacking/Item.cs @@ -0,0 +1,19 @@ +using OpenNest.Geometry; + +namespace OpenNest.CirclePacking +{ + internal class Item : Circle + { + public int Id { get; set; } + + public object Clone() + { + return new Item + { + Radius = this.Radius, + Center = this.Center, + Id = this.Id + }; + } + } +} diff --git a/Source/OpenNest.Engine/NestDirection.cs b/Source/OpenNest.Engine/NestDirection.cs new file mode 100644 index 0000000..9dc801d --- /dev/null +++ b/Source/OpenNest.Engine/NestDirection.cs @@ -0,0 +1,9 @@ + +namespace OpenNest +{ + public enum NestDirection + { + Vertical, + Horizontal + } +} diff --git a/Source/OpenNest.Engine/NestEngine.cs b/Source/OpenNest.Engine/NestEngine.cs new file mode 100644 index 0000000..20feb2c --- /dev/null +++ b/Source/OpenNest.Engine/NestEngine.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using OpenNest.RectanglePacking; + +namespace OpenNest +{ + public class NestEngine + { + public NestEngine(Plate plate) + { + Plate = plate; + } + + public Plate Plate { get; set; } + + public NestDirection NestDirection { get; set; } + + public bool Fill(NestItem item) + { + var workArea = Plate.WorkArea(); + return FillArea(workArea, item); + } + + public bool Fill(NestItem item, int maxCount) + { + var workArea = Plate.WorkArea(); + return FillArea(workArea, item, maxCount); + } + + public bool FillArea(Box box, NestItem item) + { + var binItem = ConvertToRectangleItem(item); + + var bin = new Bin + { + Location = box.Location, + Size = box.Size + }; + + bin.Width += Plate.PartSpacing; + bin.Height += Plate.PartSpacing; + + var engine = new FillBestFit(bin); + engine.Fill(binItem); + + var nestItems = new List(); + nestItems.Add(item); + + var parts = ConvertToParts(bin, nestItems); + Plate.Parts.AddRange(parts); + + return parts.Count > 0; + } + + public bool FillArea(Box box, NestItem item, int maxCount) + { + var binItem = ConvertToRectangleItem(item); + + var bin = new Bin + { + Location = box.Location, + Size = box.Size + }; + + bin.Width += Plate.PartSpacing; + bin.Height += Plate.PartSpacing; + + var engine = new FillBestFit(bin); + engine.Fill(binItem, maxCount); + + var nestItems = new List(); + nestItems.Add(item); + + var parts = ConvertToParts(bin, nestItems); + Plate.Parts.AddRange(parts); + + return parts.Count > 0; + } + + public bool Pack(List items) + { + var workArea = Plate.WorkArea(); + return PackArea(workArea, items); + } + + public bool PackArea(Box box, List items) + { + var binItems = ConvertToRectangleItems(items); + + var bin = new Bin + { + Location = box.Location, + Size = box.Size + }; + + bin.Width += Plate.PartSpacing; + bin.Height += Plate.PartSpacing; + + var engine = new PackBottomLeft(bin); + engine.Pack(binItems); + + var parts = ConvertToParts(bin, items); + Plate.Parts.AddRange(parts); + + return parts.Count > 0; + } + + private List ConvertToParts(Bin bin, List items) + { + var parts = new List(); + + foreach (var item in bin.Items) + { + var nestItem = items[item.Id]; + var part = ConvertToPart(item, nestItem.Drawing); + parts.Add(part); + } + + return parts; + } + + private Part ConvertToPart(Item item, Drawing dwg) + { + var part = new Part(dwg); + + if (item.IsRotated) + part.Rotate(Angle.HalfPI); + + var boundingBox = part.Program.BoundingBox(); + var offset = item.Location - boundingBox.Location; + + part.Offset(offset); + + return part; + } + + private List ConvertToRectangleItems(List items) + { + var binItems = new List(); + + for (int i = 0; i < items.Count; i++) + { + var item = items[i]; + var binItem = ConvertToRectangleItem(item, i); + + int maxQty = (int)Math.Floor(Plate.Area() / binItem.Area()); + + int qty = item.Quantity < maxQty + ? item.Quantity + : maxQty; + + for (int j = 0; j < qty; j++) + binItems.Add(binItem.Clone() as Item); + } + + return binItems; + } + + private Item ConvertToRectangleItem(NestItem item, int id = 0) + { + var box = item.Drawing.Program.BoundingBox(); + + box.Width += Plate.PartSpacing; + box.Height += Plate.PartSpacing; + + + + return new Item + { + Id = id, + Location = box.Location, + Size = box.Size + }; + } + } +} diff --git a/Source/OpenNest.Engine/NestItem.cs b/Source/OpenNest.Engine/NestItem.cs new file mode 100644 index 0000000..af7437c --- /dev/null +++ b/Source/OpenNest.Engine/NestItem.cs @@ -0,0 +1,35 @@ +namespace OpenNest +{ + public class NestItem + { + /// + /// The drawing to be nested. + /// + public Drawing Drawing { get; set; } + + /// + /// Priority of the part determines nesting order. Highest priority will be nested first. + /// + public int Priority { get; set; } + + /// + /// The number of parts to be nested. + /// + public int Quantity { get; set; } + + /// + /// The rotation step in radians. + /// + public double StepAngle { get; set; } + + /// + /// The rotation start angle in radians. + /// + public double RotationStart { get; set; } + + /// + /// The rotation end angle in radians. + /// + public double RotationEnd { get; set; } + } +} diff --git a/Source/OpenNest.Engine/OpenNest.Engine.csproj b/Source/OpenNest.Engine/OpenNest.Engine.csproj new file mode 100644 index 0000000..a48fc03 --- /dev/null +++ b/Source/OpenNest.Engine/OpenNest.Engine.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A} + Library + Properties + OpenNest + OpenNest.Engine + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + {5a5fde8d-f8db-440e-866c-c4807e1686cf} + OpenNest.Core + + + + + \ No newline at end of file diff --git a/Source/OpenNest.Engine/Properties/AssemblyInfo.cs b/Source/OpenNest.Engine/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..77b2e6f --- /dev/null +++ b/Source/OpenNest.Engine/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenNest.Engine")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenNest.Engine")] +[assembly: AssemblyCopyright("Copyright © AJ Isaacs 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("50013ca9-b047-41f5-b519-72e523902b53")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/Source/OpenNest.Engine/RectanglePacking/Bin.cs b/Source/OpenNest.Engine/RectanglePacking/Bin.cs new file mode 100644 index 0000000..65dfc4d --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/Bin.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.RectanglePacking +{ + internal class Bin : Box + { + public Bin() + { + Items = new List(); + } + + public List Items { get; set; } + + public double Density() + { + return Items.Sum(i => i.Area()) / Area(); + } + + public object Clone() + { + return new Bin + { + Location = this.Location, + Size = this.Size, + Items = new List(Items) + }; + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/FillBestFit.cs b/Source/OpenNest.Engine/RectanglePacking/FillBestFit.cs new file mode 100644 index 0000000..63ddeeb --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/FillBestFit.cs @@ -0,0 +1,88 @@ +using System; + +namespace OpenNest.RectanglePacking +{ + internal class FillBestFit : FillEngine + { + public FillBestFit(Bin bin) + : base(bin) + { + } + + public override void Fill(Item item) + { + var bin1 = BestFitHorizontal(item); + var bin2 = BestFitVertical(item); + + if (bin1.Items.Count == bin2.Items.Count) + { + var usedArea1 = bin1.Items.GetBoundingBox().Area(); + var usedArea2 = bin2.Items.GetBoundingBox().Area(); + + if (usedArea2 < usedArea1) + Bin.Items.AddRange(bin2.Items); + else + Bin.Items.AddRange(bin1.Items); + } + else if (bin1.Items.Count > bin2.Items.Count) + Bin.Items.AddRange(bin1.Items); + else + Bin.Items.AddRange(bin2.Items); + } + + public override void Fill(Item item, int maxCount) + { + throw new NotImplementedException(); + } + + private Bin BestFitHorizontal(Item item) + { + var bin = Bin.Clone() as Bin; + + int normalColumns = 0; + int rotateColumns = 0; + + if (!BestCombination.FindFrom2(item.Width, item.Height, bin.Width, out normalColumns, out rotateColumns)) + return bin; + + var normalRows = (int)Math.Floor((bin.Height + Tolerance.Epsilon) / item.Height); + var rotateRows = (int)Math.Floor((bin.Height + Tolerance.Epsilon) / item.Width); + + item.Location = bin.Location; + + bin.Items.AddRange(VPattern(item, normalRows, normalColumns, int.MaxValue)); + + item.Location.X += item.Width * normalColumns; + item.Rotate(); + + bin.Items.AddRange(VPattern(item, rotateRows, rotateColumns, int.MaxValue)); + + return bin; + } + + private Bin BestFitVertical(Item item) + { + var bin = Bin.Clone() as Bin; + + int normalRows = 0; + int rotateRows = 0; + + if (!BestCombination.FindFrom2(item.Height, item.Width, Bin.Height, out normalRows, out rotateRows)) + return bin; + + var normalColumns = (int)Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width); + var rotateColumns = (int)Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Height); + + item.Location = bin.Location; + + bin.Items.AddRange(VPattern(item, normalRows, normalColumns, int.MaxValue)); + + item.Location.Y += item.Height * normalRows; + item.Rotate(); + + bin.Items.AddRange(VPattern(item, rotateRows, rotateColumns, int.MaxValue)); + + return bin; + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/FillEngine.cs b/Source/OpenNest.Engine/RectanglePacking/FillEngine.cs new file mode 100644 index 0000000..d071e31 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/FillEngine.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; + +namespace OpenNest.RectanglePacking +{ + internal abstract class FillEngine + { + public FillEngine(Bin bin) + { + Bin = bin; + } + + public Bin Bin { get; set; } + + public abstract void Fill(Item item); + + public abstract void Fill(Item item, int maxCount); + + /// + /// Vertical pattern. + /// + /// + /// + /// + /// + protected List VPattern(Item item, int rows, int columns, int maxCount) + { + var items = new List(); + + for (int i = 0; i < columns; i++) + { + var x = item.Width * i + item.X; + + for (int j = 0; j < rows; j++) + { + var y = item.Height * j + item.Y; + + var addedItem = item.Clone() as Item; + addedItem.Location = new Vector(x, y); + + items.Add(addedItem); + + if (items.Count == maxCount) + return items; + } + } + + return items; + } + + /// + /// Horizontal pattern. + /// + /// + /// + /// + /// + protected List HPattern(Item item, int rows, int columns, int maxCount) + { + var items = new List(); + + for (int i = 0; i < rows; i++) + { + var y = item.Height * i + item.Y; + + for (int j = 0; j < rows; j++) + { + var x = item.Width * j + item.X; + + var addedItem = item.Clone() as Item; + addedItem.Location = new Vector(x, y); + + items.Add(addedItem); + + if (items.Count == maxCount) + return items; + } + } + + return items; + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/FillNoRotation.cs b/Source/OpenNest.Engine/RectanglePacking/FillNoRotation.cs new file mode 100644 index 0000000..71470b9 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/FillNoRotation.cs @@ -0,0 +1,68 @@ +using System; + +namespace OpenNest.RectanglePacking +{ + internal class FillNoRotation : FillEngine + { + public FillNoRotation(Bin bin) + : base(bin) + { + } + + public NestDirection NestDirection { get; set; } + + public override void Fill(Item item) + { + var ycount = (int)Math.Floor((Bin.Height + Tolerance.Epsilon) / item.Height); + var xcount = (int)Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width); + var count = ycount * xcount; + + for (int i = 0; i < xcount; i++) + { + var x = item.Width * i + Bin.X; + + for (int j = 0; j < ycount; j++) + { + var y = item.Height * j + Bin.Y; + + var addedItem = item.Clone() as Item; + addedItem.Location = new Vector(x, y); + + Bin.Items.Add(addedItem); + } + } + } + + public override void Fill(Item item, int maxCount) + { + var ycount = (int)Math.Floor((Bin.Height + Tolerance.Epsilon) / item.Height); + var xcount = (int)Math.Floor((Bin.Width + Tolerance.Epsilon) / item.Width); + var count = ycount * xcount; + + if (count <= maxCount) + { + Fill(item); + return; + } + + var columns = 0; + var rows = 0; + + if (NestDirection == NestDirection.Vertical) + { + columns = (int)Math.Ceiling((double)maxCount / ycount); + rows = (int)Math.Ceiling((double)maxCount / columns); + } + else + { + rows = (int)Math.Ceiling((double)maxCount / xcount); + columns = (int)Math.Ceiling((double)maxCount / rows); + } + + if (item.Width > item.Height) + VPattern(item, rows, columns, maxCount); + else + HPattern(item, rows, columns, maxCount); + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/FillSameRotation.cs b/Source/OpenNest.Engine/RectanglePacking/FillSameRotation.cs new file mode 100644 index 0000000..9ba17d2 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/FillSameRotation.cs @@ -0,0 +1,65 @@ + +namespace OpenNest.RectanglePacking +{ + internal class FillSameRotation : FillEngine + { + public FillSameRotation(Bin bin) + : base(bin) + { + } + + public override void Fill(Item item) + { + var bin1 = Bin.Clone() as Bin; + var bin2 = Bin.Clone() as Bin; + + var engine = new FillNoRotation(bin1); + engine.Fill(item); + + item.Rotate(); + + engine.Bin = bin2; + engine.Fill(item); + + var density1 = bin1.Density(); + var density2 = bin2.Density(); + + if (density1.IsEqualTo(density2)) + { + var bounds1 = bin1.Items.GetBoundingBox(); + var bounds2 = bin2.Items.GetBoundingBox(); + + if (bounds2.Right < bounds1.Right) + Bin.Items.AddRange(bin2.Items); + else + Bin.Items.AddRange(bin1.Items); + } + else if (density1 > density2) + Bin.Items.AddRange(bin1.Items); + else + Bin.Items.AddRange(bin2.Items); + } + + public override void Fill(Item item, int maxCount) + { + var bin1 = Bin.Clone() as Bin; + var bin2 = Bin.Clone() as Bin; + + var engine = new FillNoRotation(bin1); + engine.Fill(item, maxCount); + + item.Rotate(); + + engine.Bin = bin2; + engine.Fill(item, maxCount); + + var bounds1 = bin1.Items.GetBoundingBox(); + var bounds2 = bin2.Items.GetBoundingBox(); + + if (bounds2.Right < bounds1.Right) + Bin.Items.AddRange(bin2.Items); + else + Bin.Items.AddRange(bin1.Items); + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/Item.cs b/Source/OpenNest.Engine/RectanglePacking/Item.cs new file mode 100644 index 0000000..21d7058 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/Item.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +namespace OpenNest.RectanglePacking +{ + internal class Item : Box + { + public int Id { get; set; } + + public bool IsRotated { get; private set; } + + public void Rotate() + { + Generic.Swap(ref Size.Width, ref Size.Height); + IsRotated = !IsRotated; + } + + public object Clone() + { + return new Item + { + IsRotated = this.IsRotated, + Location = this.Location, + Size = this.Size, + Id = this.Id + }; + } + } + + internal static class ItemListExtensions + { + public static Box GetBoundingBox(this IList items) + { + if (items.Count == 0) + return Box.Empty; + + double minX = items[0].X; + double minY = items[0].Y; + double maxX = items[0].X + items[0].Width; + double maxY = items[0].Y + items[0].Height; + + foreach (var box in items) + { + if (box.Left < minX) minX = box.Left; + if (box.Right > maxX) maxX = box.Right; + if (box.Bottom < minY) minY = box.Bottom; + if (box.Top > maxY) maxY = box.Top; + } + + return new Box(minX, minY, maxX - minX, maxY - minY); + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/PackBottomLeft.cs b/Source/OpenNest.Engine/RectanglePacking/PackBottomLeft.cs new file mode 100644 index 0000000..8ca5268 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/PackBottomLeft.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.RectanglePacking +{ + internal class PackBottomLeft : PackEngine + { + private List points; + + public PackBottomLeft(Bin bin) + : base(bin) + { + points = new List(); + } + + public override void Pack(List items) + { + items = items.OrderBy(i => -i.Area()).ToList(); + + points.Add(Bin.Location); + + var skip = new List(); + + for (int i = 0; i < items.Count; i++) + { + var item = items[i]; + + if (skip.Contains(item.Id)) + continue; + + var pt = FindPointVertical(item); + + if (pt == null) + { + skip.Add(item.Id); + continue; + } + + item.Location = pt.Value; + + points.Remove(pt.Value); + points.Add(new Vector(item.Left, item.Top)); + points.Add(new Vector(item.Right, item.Bottom)); + + Bin.Items.Add(item); + } + + points.Clear(); + } + + private Vector? FindPointVertical(Item item) + { + var pt = new Vector(double.MaxValue, double.MaxValue); + + for (int i = 0; i < points.Count; i++) + { + var point = points[i]; + + item.Location = point; + + if (!IsValid(item)) + continue; + + if (point.X < pt.X) + pt = point; + else if (point.X.IsEqualTo(pt.X) && point.Y < pt.Y) + pt = point; + } + + if (pt.X != double.MaxValue && pt.Y != double.MaxValue) + return pt; + + return null; + } + + private Vector? FindPointHorizontal(Item item) + { + var pt = new Vector(double.MaxValue, double.MaxValue); + + for (int i = 0; i < points.Count; i++) + { + var point = points[i]; + + item.Location = point; + + if (!IsValid(item)) + continue; + + if (point.Y < pt.Y) + pt = point; + else if (point.Y.IsEqualTo(pt.Y) && point.X < pt.X) + pt = point; + } + + if (pt.X != double.MaxValue && pt.Y != double.MaxValue) + return pt; + + return null; + } + + private bool IsValid(Item item) + { + if (!Bin.Contains(item)) + return false; + + foreach (var it in Bin.Items) + { + if (item.Intersects(it)) + return false; + } + + return true; + } + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/PackEngine.cs b/Source/OpenNest.Engine/RectanglePacking/PackEngine.cs new file mode 100644 index 0000000..9c4f7b3 --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/PackEngine.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace OpenNest.RectanglePacking +{ + internal abstract class PackEngine + { + public PackEngine(Bin bin) + { + Bin = bin; + } + + public Bin Bin { get; set; } + + public abstract void Pack(List items); + } +} diff --git a/Source/OpenNest.Engine/RectanglePacking/PackFirstFitDecreasing.cs b/Source/OpenNest.Engine/RectanglePacking/PackFirstFitDecreasing.cs new file mode 100644 index 0000000..dc7678e --- /dev/null +++ b/Source/OpenNest.Engine/RectanglePacking/PackFirstFitDecreasing.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.RectanglePacking +{ + internal class FirstFitDecreasing : PackEngine + { + private readonly List levels; + + public FirstFitDecreasing(Bin bin) + : base(bin) + { + levels = new List(); + } + + public override void Pack(List items) + { + items = items.OrderBy(i => -i.Height).ToList(); + + foreach (var item in items) + { + if (item.Height > Bin.Height) + continue; + + var level = FindLevel(item); + + if (level == null) + continue; + + level.AddItem(item); + } + } + + private Level FindLevel(Item item) + { + foreach (var level in levels) + { + if (level.Height < item.Height) + continue; + + if (level.RemainingWidth < item.Width) + continue; + + return level; + } + + return CreateNewLevel(item); + } + + private Level CreateNewLevel(Item item) + { + var y = Bin.Y; + var lastLevel = levels.LastOrDefault(); + + if (lastLevel != null) + y = lastLevel.Y + lastLevel.Height; + + var remaining = Bin.Top - y; + + if (remaining < item.Height) + return null; + + var level = new Level(Bin); + level.Y = y; + level.Height = item.Height; + + levels.Add(level); + + return level; + } + + private class Level + { + public Level(Bin parent) + { + Parent = parent; + NextItemLocation = parent.Location; + } + + public Bin Parent { get; set; } + + private Vector NextItemLocation; + + public double X + { + get { return Parent.X; } + } + + public double Y + { + get { return NextItemLocation.Y; } + set { NextItemLocation.Y = value; } + } + + public double Width + { + get { return Parent.Width; } + } + + public double Height { get; set; } + + public double Top + { + get { return Y + Height; } + } + + public double RemainingWidth + { + get { return X + Width - NextItemLocation.X; } + } + + public void AddItem(Item item) + { + item.Location = NextItemLocation; + Parent.Items.Add(item); + + NextItemLocation = new Vector(NextItemLocation.X + item.Width, NextItemLocation.Y); + } + } + } +} diff --git a/Source/OpenNest.sln b/Source/OpenNest.sln new file mode 100644 index 0000000..c0e658e --- /dev/null +++ b/Source/OpenNest.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest", "OpenNest\OpenNest.csproj", "{1F1E40E0-5C53-474F-A258-69C9C3FAC15A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Core", "OpenNest.Core\OpenNest.Core.csproj", "{5A5FDE8D-F8DB-440E-866C-C4807E1686CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenNest.Engine", "OpenNest.Engine\OpenNest.Engine.csproj", "{0083B9CC-54AD-4085-A30D-56BC6834B71A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A}.Release|Any CPU.Build.0 = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A5FDE8D-F8DB-440E-866C-C4807E1686CF}.Release|Any CPU.Build.0 = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0083B9CC-54AD-4085-A30D-56BC6834B71A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/OpenNest/Actions/Action.cs b/Source/OpenNest/Actions/Action.cs new file mode 100644 index 0000000..2df37da --- /dev/null +++ b/Source/OpenNest/Actions/Action.cs @@ -0,0 +1,20 @@ +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + public abstract class Action + { + protected PlateView plateView; + + protected Action(PlateView plateView) + { + this.plateView = plateView; + } + + public abstract void DisconnectEvents(); + + public abstract void CancelAction(); + + public abstract bool IsBusy(); + } +} diff --git a/Source/OpenNest/Actions/ActionAddPart.cs b/Source/OpenNest/Actions/ActionAddPart.cs new file mode 100644 index 0000000..40034c5 --- /dev/null +++ b/Source/OpenNest/Actions/ActionAddPart.cs @@ -0,0 +1,136 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Add Parts")] + public class ActionAddPart : Action + { + private LayoutPart part; + private double lastScale; + + public ActionAddPart(PlateView plateView) + : this(plateView, null) + { + } + + public ActionAddPart(PlateView plateView, Drawing drawing) + : base(plateView) + { + plateView.KeyDown += plateView_KeyDown; + plateView.MouseMove += plateView_MouseMove; + plateView.MouseDown += plateView_MouseDown; + plateView.Paint += plateView_Paint; + + part = LayoutPart.Create(new Part(drawing), plateView); + part.IsSelected = true; + + lastScale = double.NaN; + + plateView.SelectedParts.Clear(); + plateView.SelectedParts.Add(part); + } + + private void plateView_MouseDown(object sender, MouseEventArgs e) + { + switch (e.Button) + { + case MouseButtons.Left: + Apply(); + break; + } + } + + private void plateView_KeyDown(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F1: + case Keys.Enter: + Apply(); + break; + + case Keys.F: + if ((Control.ModifierKeys & Keys.Control) == Keys.Control) + Fill(); + break; + } + } + + private void plateView_Paint(object sender, PaintEventArgs e) + { + if (plateView.ViewScale != lastScale) + { + part.Update(plateView); + part.Draw(e.Graphics); + } + else + { + if (part.IsDirty) + part.Update(plateView); + + part.Draw(e.Graphics); + } + + lastScale = plateView.ViewScale; + } + + private void plateView_MouseMove(object sender, MouseEventArgs e) + { + var offset = plateView.CurrentPoint - part.BoundingBox.Location; + part.Offset(offset); + plateView.Invalidate(); + } + + public override void DisconnectEvents() + { + plateView.KeyDown -= plateView_KeyDown; + plateView.MouseMove -= plateView_MouseMove; + plateView.MouseDown -= plateView_MouseDown; + plateView.Paint -= plateView_Paint; + + plateView.SelectedParts.Clear(); + plateView.Invalidate(); + } + + public override void CancelAction() + { + } + + public override bool IsBusy() + { + return false; + } + + private void Fill() + { + var boxes = new List(); + + foreach (var part in plateView.Plate.Parts) + boxes.Add(part.BoundingBox.Offset(plateView.Plate.PartSpacing)); + + var bounds = plateView.Plate.WorkArea(); + + var vbox = Helper.GetLargestBoxVertically(plateView.CurrentPoint, bounds, boxes); + var hbox = Helper.GetLargestBoxHorizontally(plateView.CurrentPoint, bounds, boxes); + + var box = vbox.Area() > hbox.Area() ? vbox : hbox; + + var engine = new NestEngine(plateView.Plate); + engine.FillArea(box, new NestItem { Drawing = this.part.BasePart.BaseDrawing }); + } + + private void Apply() + { + if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) + { + plateView.PushSelected(PushDirection.Left); + plateView.PushSelected(PushDirection.Down); + } + + plateView.Plate.Parts.Add(part.BasePart.Clone() as Part); + } + } +} diff --git a/Source/OpenNest/Actions/ActionClone.cs b/Source/OpenNest/Actions/ActionClone.cs new file mode 100644 index 0000000..7cc73ed --- /dev/null +++ b/Source/OpenNest/Actions/ActionClone.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Clone Parts")] + public class ActionClone : Action + { + private readonly List parts; + + private double lastScale; + + public ActionClone(PlateView plateView, List partsToClone) + : base(plateView) + { + plateView.KeyDown += plateView_KeyDown; + plateView.MouseMove += plateView_MouseMove; + plateView.MouseDown += plateView_MouseDown; + plateView.Paint += plateView_Paint; + + parts = new List(); + lastScale = double.NaN; + + for (int i = 0; i < partsToClone.Count; i++) + { + var part = LayoutPart.Create(partsToClone[i].Clone() as Part, plateView); + part.IsSelected = true; + parts.Add(part); + } + + plateView.SelectedParts.Clear(); + plateView.SelectedParts.AddRange(parts); + } + + private void plateView_MouseDown(object sender, MouseEventArgs e) + { + switch (e.Button) + { + case MouseButtons.Left: + Apply(); + break; + } + } + + private void plateView_KeyDown(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.F1: + case Keys.Enter: + Apply(); + break; + } + } + + private void plateView_Paint(object sender, PaintEventArgs e) + { + if (plateView.ViewScale != lastScale) + { + parts.ForEach(p => + { + p.Update(plateView); + p.Draw(e.Graphics); + }); + } + else + { + parts.ForEach(p => + { + if (p.IsDirty) + p.Update(plateView); + + p.Draw(e.Graphics); + }); + } + + lastScale = plateView.ViewScale; + } + + private void plateView_MouseMove(object sender, MouseEventArgs e) + { + var offset = plateView.CurrentPoint - parts.GetBoundingBox().Location; + parts.ForEach(p => p.Offset(offset)); + plateView.Invalidate(); + } + + public override void DisconnectEvents() + { + plateView.KeyDown -= plateView_KeyDown; + plateView.MouseMove -= plateView_MouseMove; + plateView.MouseDown -= plateView_MouseDown; + plateView.Paint -= plateView_Paint; + + plateView.SelectedParts.Clear(); + plateView.Invalidate(); + } + + public override void CancelAction() + { + } + + public override bool IsBusy() + { + return false; + } + + public void Apply() + { + if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) + { + plateView.PushSelected(PushDirection.Left); + plateView.PushSelected(PushDirection.Down); + } + + parts.ForEach(p => plateView.Plate.Parts.Add(p.BasePart.Clone() as Part)); + } + } +} diff --git a/Source/OpenNest/Actions/ActionFillArea.cs b/Source/OpenNest/Actions/ActionFillArea.cs new file mode 100644 index 0000000..ba64af2 --- /dev/null +++ b/Source/OpenNest/Actions/ActionFillArea.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Fill Area")] + public class ActionFillArea : ActionSelectArea + { + private Drawing drawing; + + public ActionFillArea(PlateView plateView, Drawing drawing) + : base(plateView) + { + plateView.PreviewKeyDown += plateView_PreviewKeyDown; + this.drawing = drawing; + } + + private void plateView_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e) + { + if (e.KeyCode == Keys.Enter) + FillArea(); + } + + private void FillArea() + { + var engine = new NestEngine(plateView.Plate); + engine.FillArea(SelectedArea, new NestItem + { + Drawing = drawing + }); + + plateView.Invalidate(); + Update(); + } + + public override void DisconnectEvents() + { + plateView.PreviewKeyDown -= plateView_PreviewKeyDown; + base.DisconnectEvents(); + } + } +} diff --git a/Source/OpenNest/Actions/ActionSelect.cs b/Source/OpenNest/Actions/ActionSelect.cs new file mode 100644 index 0000000..3ffb488 --- /dev/null +++ b/Source/OpenNest/Actions/ActionSelect.cs @@ -0,0 +1,252 @@ +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Select")] + public class ActionSelect : Action + { + private readonly Pen borderPenContain; + private readonly Pen borderPenIntersect; + private readonly Brush fillBrushContain; + private readonly Brush fillBrushIntersect; + + private Pen borderPen; + private Brush fillBrush; + + public Vector Point1; + public Vector Point2; + + private Status status; + + public ActionSelect(PlateView plateView) + : base(plateView) + { + borderPenIntersect = new Pen(Color.FromArgb(50, 255, 50)); + fillBrushIntersect = new SolidBrush(Color.FromArgb(50, 50, 255, 50)); + + borderPenContain = new Pen(Color.FromArgb(99, 162, 228)); + fillBrushContain = new SolidBrush(Color.FromArgb(90, 150, 200, 255)); + + UpdateBrushAndPen(); + + status = Status.SetFirstPoint; + + plateView.MouseDown += plateView_MouseDown; + plateView.MouseUp += plateView_MouseUp; + plateView.MouseMove += plateView_MouseMove; + plateView.Paint += plateView_Paint; + } + + public override void DisconnectEvents() + { + plateView.MouseDown -= plateView_MouseDown; + plateView.MouseUp -= plateView_MouseUp; + plateView.MouseMove -= plateView_MouseMove; + plateView.Paint -= plateView_Paint; + } + + public override void CancelAction() + { + status = Status.SetFirstPoint; + + plateView.DeselectAll(); + plateView.Invalidate(); + } + + public override bool IsBusy() + { + return status != Status.SetFirstPoint || plateView.SelectedParts.Count > 0; + } + + private void plateView_MouseMove(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Left) + return; + + if (status == Status.SetSecondPoint) + { + Point2 = plateView.PointControlToWorld(e.Location); + UpdateBrushAndPen(); + } + else + { + if (plateView.SelectedParts.Count == 0) + return; + + var offset = plateView.CurrentPoint - plateView.LastPoint; + + foreach (var part in plateView.SelectedParts) + part.Offset(offset.X, offset.Y); + } + + plateView.Invalidate(); + } + + private void plateView_MouseDown(object sender, MouseEventArgs e) + { + if (!plateView.AllowSelect) + return; + + if (e.Button == MouseButtons.Left) + { + if (plateView.SelectedParts.Count > 0) + { + var part = plateView.GetPartAtControlPoint(e.Location); + + if (part == null) + CancelAction(); + } + + if (SelectPartAtCurrentPoint()) + return; + + if (status == Status.SetFirstPoint) + { + Point1 = plateView.PointControlToWorld(e.Location); + Point2 = Point1; + status = Status.SetSecondPoint; + } + } + else if (e.Button == MouseButtons.Right) + { + CancelAction(); + } + } + + private void plateView_MouseUp(object sender, MouseEventArgs e) + { + if (!plateView.AllowSelect) + return; + + if (e.Button != MouseButtons.Left) + return; + + if (status == Status.SetSecondPoint) + { + Point2 = plateView.PointControlToWorld(e.Location); + SelectPartsInWindow(); + + plateView.Invalidate(); + status = Status.SetFirstPoint; + } + } + + private void plateView_Paint(object sender, PaintEventArgs e) + { + if (status != Status.SetSecondPoint) + return; + + var rect = GetRectangle(); + + e.Graphics.FillRectangle(fillBrush, rect); + + e.Graphics.DrawRectangle(borderPen, + rect.X, + rect.Y, + rect.Width, + rect.Height); + } + + private bool SelectPartAtCurrentPoint() + { + var part = plateView.GetPartAtPoint(plateView.CurrentPoint); + + if (part == null) + { + plateView.DeselectAll(); + status = Status.SetFirstPoint; + return false; + } + + if (Control.ModifierKeys != Keys.Control && plateView.SelectedParts.Contains(part) == false) + plateView.DeselectAll(); + + if (plateView.SelectedParts.Contains(part) == false) + { + plateView.SelectedParts.Add(part); + part.IsSelected = true; + } + + plateView.Invalidate(); + return true; + } + + private void SelectPartsInWindow() + { + var parts = plateView.GetPartsFromWindow(GetRectangle(), SelectionType); + + foreach (var part in parts) + { + if (plateView.SelectedParts.Contains(part)) + continue; + + plateView.SelectedParts.Add(part); + part.IsSelected = true; + } + } + + private void UpdateBrushAndPen() + { + if (SelectionType == SelectionType.Intersect) + { + borderPen = borderPenIntersect; + fillBrush = fillBrushIntersect; + } + else + { + borderPen = borderPenContain; + fillBrush = fillBrushContain; + } + } + + private RectangleF GetRectangle() + { + var rect = new RectangleF(); + var pt1 = plateView.PointWorldToGraph(Point1); + var pt2 = plateView.PointWorldToGraph(Point2); + + if (pt1.X < pt2.X) + { + rect.X = pt1.X; + rect.Width = pt2.X - pt1.X; + } + else + { + rect.X = pt2.X; + rect.Width = pt1.X - pt2.X; + } + + if (pt1.Y < pt2.Y) + { + rect.Y = pt1.Y; + rect.Height = pt2.Y - pt1.Y; + } + else + { + rect.Y = pt2.Y; + rect.Height = pt1.Y - pt2.Y; + } + + return rect; + } + + public SelectionType SelectionType + { + get + { + return Point1.X < Point2.X + ? SelectionType.Contains + : SelectionType.Intersect; + } + } + + public enum Status + { + SetFirstPoint, + SetSecondPoint + } + } +} diff --git a/Source/OpenNest/Actions/ActionSelectArea.cs b/Source/OpenNest/Actions/ActionSelectArea.cs new file mode 100644 index 0000000..692a624 --- /dev/null +++ b/Source/OpenNest/Actions/ActionSelectArea.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Select Area")] + public class ActionSelectArea : Action + { + public Box SelectedArea { get; private set; } + private Box Bounds { get; set; } + private List boxes { get; set; } + + private bool dynamicSelect; + private bool altSelect; + + private readonly Pen pen; + private readonly Brush brush; + + private readonly Font font; + private readonly StringFormat stringFormat; + + public ActionSelectArea(PlateView plateView) + : base(plateView) + { + boxes = new List(); + pen = new Pen(Color.FromArgb(50, 255, 50)); + brush = new SolidBrush(Color.FromArgb(50, 50, 255, 50)); + font = new Font(SystemFonts.DefaultFont.FontFamily, 14); + + stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + + SelectedArea = Box.Empty; + dynamicSelect = true; + + Update(); + + if (dynamicSelect) + plateView.MouseMove += plateView_MouseMove; + else + plateView.MouseClick += plateView_MouseClick; + + plateView.KeyUp += plateView_KeyUp; + plateView.Paint += plateView_Paint; + } + + private void plateView_KeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Space: + altSelect = !altSelect; + UpdateSelectedArea(); + break; + } + } + + public bool DynamicSelect + { + get { return dynamicSelect; } + set + { + if (value == dynamicSelect) + return; + + dynamicSelect = value; + + if (dynamicSelect) + { + plateView.MouseMove += plateView_MouseMove; + plateView.MouseClick -= plateView_MouseClick; + } + else + { + plateView.MouseMove -= plateView_MouseMove; + plateView.MouseClick += plateView_MouseClick; + } + } + } + + private void plateView_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + if (SelectedArea == Box.Empty) + return; + + var location = plateView.PointWorldToGraph(SelectedArea.Location); + var size = new SizeF( + plateView.LengthWorldToGui(SelectedArea.Width), + plateView.LengthWorldToGui(SelectedArea.Height)); + + var rect = new System.Drawing.RectangleF(location.X, location.Y - size.Height, size.Width, size.Height); + + e.Graphics.DrawRectangle(pen, + rect.X, + rect.Y, + rect.Width, + rect.Height); + + e.Graphics.FillRectangle(brush, rect); + + e.Graphics.DrawString( + SelectedArea.Size.ToString(2), + font, + Brushes.Green, + rect, + stringFormat); + } + + private void plateView_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + UpdateSelectedArea(); + } + + private void plateView_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (e.Button != System.Windows.Forms.MouseButtons.Left) + return; + + UpdateSelectedArea(); + plateView.Invalidate(); + } + + public override void DisconnectEvents() + { + plateView.KeyUp -= plateView_KeyUp; + plateView.MouseMove -= plateView_MouseMove; + plateView.MouseClick -= plateView_MouseClick; + plateView.Paint -= plateView_Paint; + } + + public override void CancelAction() + { + plateView.Invalidate(); + } + + public override bool IsBusy() + { + return false; + } + + private void UpdateSelectedArea() + { + SelectedArea = altSelect + ? Helper.GetLargestBoxHorizontally(plateView.CurrentPoint, Bounds, boxes) + : Helper.GetLargestBoxVertically(plateView.CurrentPoint, Bounds, boxes); + + plateView.Invalidate(); + } + + public void Update() + { + foreach (var part in plateView.Plate.Parts) + boxes.Add(part.BoundingBox.Offset(plateView.Plate.PartSpacing)); + + Bounds = plateView.Plate.WorkArea(); + } + } +} diff --git a/Source/OpenNest/Actions/ActionSetSequence.cs b/Source/OpenNest/Actions/ActionSetSequence.cs new file mode 100644 index 0000000..7dfb746 --- /dev/null +++ b/Source/OpenNest/Actions/ActionSetSequence.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using OpenNest.Controls; +using OpenNest.Forms; +using OpenNest.Geometry; + +namespace OpenNest.Actions +{ + [DisplayName("Set Sequence")] + public class ActionSetSequence : Action + { + private readonly SequenceForm SequenceForm; + private readonly List ShapePartPairs; + private readonly Pen pen; + + private Shape ClosestShape; + private Part ClosestPart; + private int sequenceNumber = 1; + + public int SequenceNumber + { + get { return sequenceNumber; } + set + { + if (value <= SequenceForm.numericUpDown1.Maximum && value >= SequenceForm.numericUpDown1.Minimum) + { + sequenceNumber = value; + SequenceForm.numericUpDown1.Value = sequenceNumber; + } + } + } + + public ActionSetSequence(PlateView plateView) + : base(plateView) + { + SequenceForm = new Forms.SequenceForm(); + SequenceForm.numericUpDown1.DataBindings.Add("Value", this, "SequenceNumber", false, DataSourceUpdateMode.OnPropertyChanged); + SequenceForm.numericUpDown1.Maximum = plateView.Plate.Parts.Count; + SequenceForm.Owner = Application.OpenForms[0]; + SequenceForm.Show(); + plateView.Invalidate(); + + pen = new Pen(Color.Blue, 2.0f); + + ShapePartPairs = new List(); + + foreach (var part in plateView.Plate.Parts) + { + var entities = ConvertProgram.ToGeometry(part.Program).Where(e => e.Layer == SpecialLayers.Cut).ToList(); + entities.ForEach(entity => entity.Offset(part.Location)); + var shapes = Helper.GetShapes(entities); + var shape = new Shape(); + shape.Entities.AddRange(shapes); + ShapePartPairs.Add(new Pair() { Part = part, Shape = shape }); + } + + plateView.MouseMove += plateView_MouseMove; + plateView.MouseClick += plateView_MouseClick; + plateView.Paint += plateView_Paint; + + SequenceNumber = 1; + } + + private void plateView_MouseClick(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Right) + CancelAction(); + + if (e.Button != MouseButtons.Left) + return; + + if (SequenceNumber > plateView.Plate.Parts.Count) + SequenceNumber = plateView.Plate.Parts.Count; + + if (ClosestPart == null) + return; + + plateView.Plate.Parts.Remove(ClosestPart); + plateView.Plate.Parts.Insert(SequenceNumber - 1, ClosestPart); + + SequenceNumber++; + + plateView.Invalidate(); + } + + private void plateView_MouseMove(object sender, MouseEventArgs e) + { + var pair = GetClosestShape(); + ClosestShape = pair.HasValue ? pair.Value.Shape : null; + ClosestPart = pair.HasValue ? pair.Value.Part : null; + plateView.Invalidate(); + } + + private void plateView_Paint(object sender, PaintEventArgs e) + { + if (ClosestShape == null) return; + + var path = ClosestShape.GetGraphicsPath(); + path.Transform(plateView.Matrix); + e.Graphics.DrawPath(pen, path); + path.Dispose(); + } + + private Pair? GetClosestShape() + { + Pair? closestShape = null; + double distance = double.MaxValue; + + for (int i = 0; i < ShapePartPairs.Count; i++) + { + var shape = ShapePartPairs[i]; + var closestPt = shape.Shape.ClosestPointTo(plateView.CurrentPoint); + var distance2 = closestPt.DistanceTo(plateView.CurrentPoint); + + if (distance2 < distance) + { + closestShape = shape; + distance = distance2; + } + } + + return closestShape; + } + + public override void DisconnectEvents() + { + plateView.Paint -= plateView_Paint; + plateView.MouseMove -= plateView_MouseMove; + plateView.MouseClick -= plateView_MouseClick; + plateView.Invalidate(); + + SequenceForm.Close(); + } + + public override void CancelAction() + { + SequenceNumber = 1; + SequenceForm.numericUpDown1.Value = SequenceNumber + 1; + } + + public override bool IsBusy() + { + return false; + } + + private struct Pair + { + public Part Part; + public Shape Shape; + } + } +} diff --git a/Source/OpenNest/Actions/ActionZoomWindow.cs b/Source/OpenNest/Actions/ActionZoomWindow.cs new file mode 100644 index 0000000..ec2032d --- /dev/null +++ b/Source/OpenNest/Actions/ActionZoomWindow.cs @@ -0,0 +1,205 @@ +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest.Actions +{ + [DisplayName("Zoom Window")] + public class ActionZoomWindow : Action + { + public Vector Point1; + public Vector Point2; + + private readonly Pen borderPen; + private readonly Brush fillBrush; + + private Status status; + + public ActionZoomWindow(PlateView plateView) + : base(plateView) + { + borderPen = new Pen(Color.FromArgb(99, 162, 228)); + fillBrush = new SolidBrush(Color.FromArgb(90, 150, 200, 255)); + + status = Status.SetFirstPoint; + + plateView.Cursor = Cursors.Arrow; + plateView.MouseDown += plateView_MouseDown; + plateView.MouseUp += plateView_MouseUp; + plateView.MouseMove += plateView_MouseMove; + plateView.Paint += plateView_Paint; + } + + public override void DisconnectEvents() + { + plateView.Cursor = Cursors.Cross; + plateView.MouseDown -= plateView_MouseDown; + plateView.MouseUp -= plateView_MouseUp; + plateView.MouseMove -= plateView_MouseMove; + plateView.Paint -= plateView_Paint; + } + + public override void CancelAction() + { + Point1 = Vector.Invalid; + Point2 = Vector.Invalid; + + plateView.Invalidate(); + status = Status.SetFirstPoint; + } + + public override bool IsBusy() + { + return status != Status.SetFirstPoint; + } + + private void plateView_MouseMove(object sender, MouseEventArgs e) + { + if (status == Status.SetSecondPoint || e.Button == MouseButtons.Left) + { + Point2 = plateView.PointControlToWorld(e.Location); + plateView.Invalidate(); + } + } + + private void plateView_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Right) + { + CancelAction(); + return; + } + + if (e.Button != MouseButtons.Left) + return; + + if (e.Button == MouseButtons.Left && status == Status.SetFirstPoint) + { + Point1 = plateView.PointControlToWorld(e.Location); + Point2 = Point1; + plateView.Invalidate(); + } + } + + private void plateView_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Left) + return; + + Point2 = plateView.PointControlToWorld(e.Location); + + if (status == Status.SetFirstPoint) + { + var distance1 = Point1.DistanceTo(Point2); + var distance2 = plateView.LengthWorldToGui(distance1); + + if (distance2 > 40) + { + ZoomWindow(); + CancelAction(); + } + else + { + status = Status.SetSecondPoint; + } + } + else if (status == Status.SetSecondPoint) + { + ZoomWindow(); + CancelAction(); + } + } + + private void plateView_Paint(object sender, PaintEventArgs e) + { + if (!Point1.IsValid() || !Point2.IsValid()) + return; + + var rect = GetRectangle(); + + e.Graphics.FillRectangle(fillBrush, rect); + + e.Graphics.DrawRectangle(borderPen, + rect.X, + rect.Y, + rect.Width, + rect.Height); + + var centerX = rect.X + rect.Width * 0.5f; + var centerY = rect.Y + rect.Height * 0.5f; + + const float halfWidth = 10; + + e.Graphics.DrawLine(borderPen, centerX, centerY - halfWidth, centerX, centerY + halfWidth); + e.Graphics.DrawLine(borderPen, centerX - halfWidth, centerY, centerX + halfWidth, centerY); + } + + private void ZoomWindow() + { + double x, y, w, h; + + if (Point1.X < Point2.X) + { + x = Point1.X; + w = Point2.X - Point1.X; + } + else + { + x = Point2.X; + w = Point1.X - Point2.X; + } + + if (Point1.Y < Point2.Y) + { + y = Point1.Y; + h = Point2.Y - Point1.Y; + } + else + { + y = Point2.Y; + h = Point1.Y - Point2.Y; + } + + plateView.ZoomToArea(x, y, w, h); + plateView.Refresh(); + } + + private RectangleF GetRectangle() + { + var rect = new RectangleF(); + var pt1 = plateView.PointWorldToGraph(Point1); + var pt2 = plateView.PointWorldToGraph(Point2); + + if (pt1.X < pt2.X) + { + rect.X = pt1.X; + rect.Width = pt2.X - pt1.X; + } + else + { + rect.X = pt2.X; + rect.Width = pt1.X - pt2.X; + } + + if (pt1.Y < pt2.Y) + { + rect.Y = pt1.Y; + rect.Height = pt2.Y - pt1.Y; + } + else + { + rect.Y = pt2.Y; + rect.Height = pt1.Y - pt2.Y; + } + + return rect; + } + + public enum Status + { + SetFirstPoint, + SetSecondPoint + } + } +} diff --git a/Source/OpenNest/ColorScheme.cs b/Source/OpenNest/ColorScheme.cs new file mode 100644 index 0000000..aa2148d --- /dev/null +++ b/Source/OpenNest/ColorScheme.cs @@ -0,0 +1,140 @@ +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace OpenNest +{ + public sealed class ColorScheme + { + private Color layoutOutlineColor; + private Color layoutFillColor; + private Color boundingBoxColor; + private Color rapidColor; + private Color originColor; + private Color edgeSpacingColor; + + public static readonly ColorScheme Default = new ColorScheme + { + BackgroundColor = Color.DarkGray, + LayoutOutlineColor = Color.Gray, + LayoutFillColor = Color.WhiteSmoke, + BoundingBoxColor = Color.FromArgb(128, 128, 255), + RapidColor = Color.DodgerBlue, + OriginColor = Color.Gray, + EdgeSpacingColor = Color.FromArgb(180, 180, 180), + }; + + #region Pens/Brushes + + public Pen LayoutOutlinePen { get; private set; } + + public Brush LayoutFillBrush { get; private set; } + + public Pen BoundingBoxPen { get; private set; } + + public Pen RapidPen { get; private set; } + + public Pen OriginPen { get; private set; } + + public Pen EdgeSpacingPen { get; private set; } + + #endregion Pens/Brushes + + #region Colors + + public Color BackgroundColor { get; set; } + + public Color LayoutOutlineColor + { + get { return layoutOutlineColor; } + set + { + layoutOutlineColor = value; + + if (LayoutOutlinePen != null) + LayoutOutlinePen.Dispose(); + + LayoutOutlinePen = new Pen(value); + } + } + + public Color LayoutFillColor + { + get { return layoutFillColor; } + set + { + layoutFillColor = value; + + if (LayoutFillBrush != null) + LayoutFillBrush.Dispose(); + + LayoutFillBrush = new SolidBrush(value); + } + } + + public Color BoundingBoxColor + { + get { return boundingBoxColor; } + set + { + boundingBoxColor = value; + + if (BoundingBoxPen != null) + BoundingBoxPen.Dispose(); + + BoundingBoxPen = new Pen(value); + } + } + + public Color RapidColor + { + get { return rapidColor; } + set + { + rapidColor = value; + + if (RapidPen != null) + RapidPen.Dispose(); + + RapidPen = new Pen(value) + { + DashPattern = new float[] { 10, 10 }, + DashCap = DashCap.Flat + }; + } + } + + public Color OriginColor + { + get { return originColor; } + set + { + originColor = value; + + if (OriginPen != null) + OriginPen.Dispose(); + + OriginPen = new Pen(value); + } + } + + public Color EdgeSpacingColor + { + get { return edgeSpacingColor; } + set + { + edgeSpacingColor = value; + + if (EdgeSpacingPen != null) + EdgeSpacingPen.Dispose(); + + EdgeSpacingPen = new Pen(value) + { + DashPattern = new float[] { 3, 3 }, + DashCap = DashCap.Flat + }; + } + } + + #endregion Colors + } +} diff --git a/Source/OpenNest/Controls/BottomPanel.cs b/Source/OpenNest/Controls/BottomPanel.cs new file mode 100644 index 0000000..81fbda2 --- /dev/null +++ b/Source/OpenNest/Controls/BottomPanel.cs @@ -0,0 +1,36 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class BottomPanel : Panel + { + private readonly Pen lightPen; + private readonly Pen darkPen; + + public BottomPanel() + { + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.Height = 50; + this.Dock = DockStyle.Bottom; + + lightPen = new Pen(ProfessionalColors.SeparatorLight); + darkPen = new Pen(ProfessionalColors.SeparatorDark); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + e.Graphics.DrawLine(darkPen, 0, 0, Width, 0); + e.Graphics.DrawLine(lightPen, 0, 1, Width, 1); + } + } +} diff --git a/Source/OpenNest/Controls/DrawControl.cs b/Source/OpenNest/Controls/DrawControl.cs new file mode 100644 index 0000000..8d1c94c --- /dev/null +++ b/Source/OpenNest/Controls/DrawControl.cs @@ -0,0 +1,247 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public abstract class DrawControl : Control + { + protected PointF origin; + protected Point lastPoint; + protected System.Drawing.Size lastSize; + + public Vector LastPoint { get; protected set; } + public Vector CurrentPoint { get; protected set; } + + public float ViewScale { get; protected set; } + public float ViewScaleMin { get; protected set; } + public float ViewScaleMax { get; protected set; } + + internal Matrix Matrix { get; set; } + + protected const int BorderWidth = 50; + protected const float ZoomInFactor = 1.15f; + protected const float ZoomOutFactor = 1.0f / ZoomInFactor; + + protected DrawControl() + { + ViewScale = 1.0f; + ViewScaleMin = 0.3f; + ViewScaleMax = 3000; + origin = new PointF(100, 100); + } + + /// + /// Returns a point with coordinates relative to the control from the graph. + /// + /// + /// + /// + public Point PointGraphToControl(float x, float y) + { + return new Point((int)(x + origin.X), (int)(y + origin.Y)); + } + + /// + /// Returns a point with coordinates relative to the control from the graph. + /// + /// + /// + public Point PointGraphToControl(PointF pt) + { + return PointGraphToControl(pt.X, pt.Y); + } + + /// + /// Returns a point with coordinates relative to the control from the world. + /// + /// + /// + /// + public Point PointWorldToControl(double x, double y) + { + return PointGraphToControl(PointWorldToGraph(x, y)); + } + + /// + /// Returns a point with coordinates relative to the control from the world. + /// + /// + /// + public Point PointWorldToControl(Vector pt) + { + return PointGraphToControl(PointWorldToGraph(pt.X, pt.Y)); + } + + /// + /// Returns a point with coordinates relative to the graph from the control. + /// + /// + /// + /// + public PointF PointControlToGraph(int x, int y) + { + return new PointF(x - origin.X, y - origin.Y); + } + + /// + /// Returns a point with coordinates relative to the graph from the control. + /// + /// + /// + public PointF PointControlToGraph(Point pt) + { + return PointControlToGraph(pt.X, pt.Y); + } + + /// + /// Returns a point with coordinates relative to the graph from the world. + /// + /// + /// + /// + public PointF PointWorldToGraph(double x, double y) + { + return new PointF(ViewScale * (float)x, -ViewScale * (float)y); + } + + /// + /// Returns a point with coordinates relative to the graph from the world. + /// + /// + /// + public PointF PointWorldToGraph(Vector pt) + { + return PointWorldToGraph(pt.X, pt.Y); + } + + /// + /// Returns a point with coordinates relative to the world from the control. + /// + /// + /// + /// + public Vector PointControlToWorld(int x, int y) + { + return PointGraphToWorld(PointControlToGraph(x, y)); + } + + /// + /// Returns a point with coordinates relative to the world from the control. + /// + /// + /// + public Vector PointControlToWorld(Point pt) + { + return PointGraphToWorld(PointControlToGraph(pt.X, pt.Y)); + } + + /// + /// Returns a point with coordinates relative to the world from the graph. + /// + /// + /// + /// + public Vector PointGraphToWorld(float x, float y) + { + return new Vector(x / ViewScale, y / -ViewScale); + } + + /// + /// Returns a point with coordinates relative to the world from the graph. + /// + /// + /// + public Vector PointGraphToWorld(PointF pt) + { + return PointGraphToWorld(pt.X, pt.Y); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + origin.X += (Size.Width - lastSize.Width) * 0.5f; + origin.Y += (Size.Height - lastSize.Height) * 0.5f; + + lastSize = Size; + } + + public float LengthWorldToGui(double length) + { + return ViewScale * (float)length; + } + + public double LengthGuiToWorld(float length) + { + return length / ViewScale; + } + + public virtual void ZoomToControlPoint(Point pt, float zoomFactor, bool redraw = true) + { + var pt2 = PointControlToWorld(pt); + ZoomToPoint(pt2, zoomFactor, redraw); + } + + public virtual void ZoomToPoint(Vector pt, float zoomFactor, bool redraw = true) + { + var newScale = ViewScale * zoomFactor; + + if (newScale < ViewScaleMin) + zoomFactor = ViewScaleMin / ViewScale; + else if (newScale > ViewScaleMax) + zoomFactor = ViewScaleMax / ViewScale; + + origin.X -= (float)(pt.X * zoomFactor - pt.X) * ViewScale; + origin.Y += (float)(pt.Y * zoomFactor - pt.Y) * ViewScale; + + ViewScale *= zoomFactor; + UpdateMatrix(); + + if (redraw) Invalidate(); + } + + public virtual void ZoomToArea(Box box, bool redraw = true) + { + ZoomToArea(box.X, box.Y, box.Width, box.Height, redraw); + } + + public virtual void ZoomToArea(double x, double y, double width, double height, bool redraw = true) + { + if (width <= 0 || height <= 0) + return; + + var a = (Height - BorderWidth) / height; + var b = (Width - BorderWidth) / width; + + ViewScale = (float)(a < b ? a : b); + + if (ViewScale > ViewScaleMax) + ViewScale = ViewScaleMax; + else if (ViewScale < ViewScaleMin) + ViewScale = ViewScaleMin; + + var px = LengthWorldToGui(x); + var py = LengthWorldToGui(y); + var pw = LengthWorldToGui(width); + var ph = LengthWorldToGui(height); + + origin.X = (Width - pw) * 0.5f - px; + origin.Y = (Height + ph) * 0.5f + py; + + UpdateMatrix(); + + if (redraw) Invalidate(); + } + + protected virtual void UpdateMatrix() + { + if (Matrix != null) + Matrix.Dispose(); + + Matrix = new Matrix(); + Matrix.Scale(ViewScale, -ViewScale); + } + } +} diff --git a/Source/OpenNest/Controls/DrawingListBox.cs b/Source/OpenNest/Controls/DrawingListBox.cs new file mode 100644 index 0000000..272e684 --- /dev/null +++ b/Source/OpenNest/Controls/DrawingListBox.cs @@ -0,0 +1,101 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + using Size = System.Drawing.Size; + + public class DrawingListBox : ListBox + { + private readonly Size imageSize; + private readonly Font nameFont; + private Point lastClickLocation; + + public DrawingListBox() + { + SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer, true); + + DrawMode = DrawMode.OwnerDrawFixed; + ItemHeight = 85; + + imageSize = new Size(ItemHeight, ItemHeight - 10); + nameFont = new Font(Font.FontFamily, 10, FontStyle.Bold); + } + + public Units Units { get; set; } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + if (e.Index >= Items.Count || e.Index <= -1) + return; + + var dwg = Items[e.Index] as Drawing; + + if (dwg == null) + return; + + e.Graphics.FillRectangle(Brushes.White, e.Bounds); + + var pt = new PointF(5, e.Bounds.Y + 5); + + var brush = new SolidBrush(dwg.Color); + var pen = new Pen(ControlPaint.Dark(dwg.Color)); + + var img = dwg.Program.GetImage(imageSize, pen, brush); + + pen.Dispose(); + brush.Dispose(); + + e.Graphics.DrawImage(img, pt); + + pt.X += imageSize.Width + 10; + + e.Graphics.DrawString(dwg.Name, nameFont, Brushes.Black, pt); + + var bounds = dwg.Program.BoundingBox(); + var text1 = string.Format("{0} of {1} nested", dwg.Quantity.Nested, dwg.Quantity.Required); + var text2 = bounds.Size.ToString(4); + var text3 = string.Format("{0} sq/{1}", Math.Round(dwg.Area, 4), UnitsHelper.GetShortString(Units)); + + pt.Y += 22; + e.Graphics.DrawString(text1, Font, Brushes.Gray, pt); + pt.Y += 18; + e.Graphics.DrawString(text2, Font, Brushes.Gray, pt); + pt.Y += 18; + e.Graphics.DrawString(text3, Font, Brushes.Gray, pt); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + var item = SelectedItem as Drawing; + + if (item == null) + return; + + if (e.Button == MouseButtons.Left && e.Location.DistanceTo(lastClickLocation) > 3) + DoDragDrop(item, DragDropEffects.Copy); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + lastClickLocation = e.Location; + } + } + + public static class PointExtensions + { + public static double DistanceTo(this Point pt1, Point pt2) + { + var x = pt2.X - pt1.X; + var y = pt2.Y - pt1.Y; + + return Math.Sqrt(x * x + y * y); + } + } +} diff --git a/Source/OpenNest/Controls/EntityView.cs b/Source/OpenNest/Controls/EntityView.cs new file mode 100644 index 0000000..4b32381 --- /dev/null +++ b/Source/OpenNest/Controls/EntityView.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using OpenNest.Geometry; + +namespace OpenNest.Controls +{ + public class EntityView : DrawControl + { + public List Entities; + + private Pen pen = new Pen(Color.FromArgb(70, 70, 70)); + + public EntityView() + { + Entities = new List(); + + this.BackColor = Color.FromArgb(33, 40, 48); + this.Cursor = Cursors.Cross; + + SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.UserPaint, true); + } + + protected override void OnMouseClick(MouseEventArgs e) + { + base.OnMouseClick(e); + if (!Focused) Focus(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + e.Graphics.SmoothingMode = SmoothingMode.HighSpeed; + e.Graphics.DrawLine(pen, origin.X, 0, origin.X, Height); + e.Graphics.DrawLine(pen, 0, origin.Y, Width, origin.Y); + + e.Graphics.TranslateTransform(origin.X, origin.Y); + + foreach (var entity in Entities) + DrawEntity(e.Graphics, entity, Pens.White); + +#if DRAW_OFFSET + + var offsetShape = new Shape(); + offsetShape.Entities.AddRange(Entities); + + foreach (var entity in ((Shape)offsetShape.OffsetEntity(0.25, OffsetSide.Left)).Entities) + DrawEntity(e.Graphics, entity, Pens.RoyalBlue); +#endif + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + float multiplier = Math.Abs(e.Delta / 120.0f); + + if (e.Delta > 0) + ZoomToControlPoint(e.Location, (float)Math.Pow(ZoomInFactor, multiplier)); + else + ZoomToControlPoint(e.Location, (float)Math.Pow(ZoomOutFactor, multiplier)); + + Invalidate(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if (e.Button == MouseButtons.Middle) + { + var diffx = e.X - lastPoint.X; + var diffy = e.Y - lastPoint.Y; + + origin.X += diffx; + origin.Y += diffy; + + Invalidate(); + } + else + { + LastPoint = CurrentPoint; + CurrentPoint = PointControlToWorld(e.Location); + } + + lastPoint = e.Location; + + base.OnMouseMove(e); + } + + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (e.Button == MouseButtons.Middle) + ZoomToFit(); + } + + private void DrawEntity(Graphics g, Entity e, Pen pen) + { + if (!e.Layer.IsVisible) + return; + + switch (e.Type) + { + case EntityType.Arc: + DrawArc(g, (Arc)e, pen); + break; + + case EntityType.Circle: + DrawCircle(g, (Circle)e, pen); + break; + + case EntityType.Line: + DrawLine(g, (Line)e, pen); + break; + } + } + + private void DrawLine(Graphics g, Line line, Pen pen) + { + var pt1 = PointWorldToGraph(line.StartPoint); + var pt2 = PointWorldToGraph(line.EndPoint); + + g.DrawLine(pen, pt1, pt2); + } + + private void DrawArc(Graphics g, Arc arc, Pen pen) + { + var center = PointWorldToGraph(arc.Center); + var radius = LengthWorldToGui(arc.Radius); + var diameter = radius * 2.0f; + + var startAngle = arc.IsReversed + ? -(float)Angle.ToDegrees(arc.EndAngle) + : -(float)Angle.ToDegrees(arc.StartAngle); + + g.DrawArc( + pen, + center.X - radius, + center.Y - radius, + diameter, + diameter, + startAngle, + -(float)Angle.ToDegrees(arc.SweepAngle())); + } + + private void DrawCircle(Graphics g, Circle circle, Pen pen) + { + var center = PointWorldToGraph(circle.Center); + var radius = LengthWorldToGui(circle.Radius); + var diameter = radius * 2.0f; + + g.DrawEllipse(pen, + center.X - radius, + center.Y - radius, + diameter, + diameter); + } + + private void DrawPoint(Graphics g, Vector pt, Pen pen) + { + var pt1 = PointWorldToGraph(pt); + + g.DrawLine(pen, pt1.X - 3, pt1.Y, pt1.X + 3, pt1.Y); + g.DrawLine(pen, pt1.X, pt1.Y - 3, pt1.X, pt1.Y + 3); + } + + private void DrawPoint(Graphics g, Vector pt, Pen pen, Brush fillBrush) + { + var pt1 = PointWorldToGraph(pt); + var rect = new RectangleF(pt1.X - 3, pt1.Y - 3, 6, 6); + + g.FillEllipse(fillBrush, rect); + g.DrawEllipse(pen, rect); + } + + private void DrawIntersections(Graphics g) + { + for (int i = 0; i < Entities.Count; ++i) + { + var entity1 = Entities[i]; + + if (entity1.Type != EntityType.Line) + continue; + + for (int j = i + 1; j < Entities.Count; ++j) + { + var entity2 = Entities[j]; + + if (entity2.Type != EntityType.Line) + continue; + + var line1 = (Line)entity1; + var line2 = (Line)entity2; + + Vector pt; + + if (line1.Intersects(line2, out pt)) + DrawPoint(g, pt, Pens.Yellow, Brushes.Red); + } + } + } + + public void ZoomToFit(bool redraw = true) + { + ZoomToArea(Entities.GetBoundingBox(), redraw); + } + } +} diff --git a/Source/OpenNest/Controls/HorizontalLine.cs b/Source/OpenNest/Controls/HorizontalLine.cs new file mode 100644 index 0000000..e97c840 --- /dev/null +++ b/Source/OpenNest/Controls/HorizontalLine.cs @@ -0,0 +1,38 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class HorizontalLine : Control + { + private readonly Pen lightPen; + private readonly Pen darkPen; + + public HorizontalLine() + { + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.Selectable, false); + + lightPen = new Pen(ProfessionalColors.SeparatorLight); + darkPen = new Pen(ProfessionalColors.SeparatorDark); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + float midpoint = Height * 0.5f; + + e.Graphics.DrawLine(darkPen, 0, midpoint, Width, midpoint); + midpoint++; + e.Graphics.DrawLine(lightPen, 0, midpoint, Width, midpoint); + } + } +} diff --git a/Source/OpenNest/Controls/LayoutViewGL.cs b/Source/OpenNest/Controls/LayoutViewGL.cs new file mode 100644 index 0000000..f32a2c6 --- /dev/null +++ b/Source/OpenNest/Controls/LayoutViewGL.cs @@ -0,0 +1,451 @@ +using System.Runtime.Remoting.Messaging; +using libPep; +using libPep.Codes; +using OpenTK.Graphics.OpenGL; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class LayoutViewGL : OpenTK.GLControl + { + private float scale; + private bool loaded = false; + + private const double TwoPI = Math.PI * 2.0; + private const int Resolution = 100; + private const int BorderWidth = 50; + private const float ZoomInFactor = 1.1f; + private const float ZoomOutFactor = 0.9f; + + private PointF origin; + private PointF lastPoint; + + private Vector curpos; + private ProgrammingMode mode; + + public Color RapidColor { get; set; } + public Color PlateFillColor { get; set; } + public Color PlateBorderColor { get; set; } + public Color GeometryColor { get; set; } + + public Plate Plate; + + public LayoutViewGL() + { + scale = 25; + origin = new PointF(0, 0); + lastPoint = new PointF(); + + BackColor = Color.White; + RapidColor = Color.Blue; + GeometryColor = Color.LimeGreen; + PlateFillColor = Color.WhiteSmoke; + PlateBorderColor = Color.DarkGray; + + Cursor = Cursors.Cross; + + Plate = new Plate(); + + Context.SwapInterval = 0; + } + + public bool DrawRapid { get; set; } + + private void SetupViewport() + { + int w = Width; + int h = Height; + GL.MatrixMode(MatrixMode.Projection); + GL.LoadIdentity(); + GL.Ortho(0, w, 0, h, -1, 1); + GL.Viewport(0, 0, w, h); + } + + protected override void OnLoad(EventArgs e) + { + SetupViewport(); + GL.ClearColor(this.BackColor); + loaded = true; + } + + protected override void OnPaint(PaintEventArgs e) + { + if (!loaded) + return; + + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + GL.MatrixMode(MatrixMode.Modelview); + GL.LoadIdentity(); + + //DrawAxis(); + + GL.Translate(origin.X, origin.Y, 0); + GL.Scale(scale, scale, 1); + + curpos = new Vector(); + + if (Plate != null) + { + GL.Color3(PlateFillColor); + GL.Begin(PrimitiveType.Quads); + DrawPlate(Plate); + GL.End(); + + GL.Color3(PlateBorderColor); + GL.Begin(PrimitiveType.LineLoop); + DrawPlate(Plate); + GL.End(); + + GL.Color3(GeometryColor); + DrawProgram(Plate); + } + + SwapBuffers(); + } + + protected override void OnResize(EventArgs e) + { + if (!loaded) + return; + + SetupViewport(); + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + float multiplier = Math.Abs(e.Delta / 120.0f); + + if (e.Delta > 0) + ZoomToPoint(e.X, e.Y, (float)Math.Pow(ZoomInFactor, multiplier)); + else + ZoomToPoint(e.X, e.Y, (float)Math.Pow(ZoomOutFactor, multiplier)); + + Invalidate(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (e.Button == MouseButtons.Middle) + { + var diffx = e.X - lastPoint.X; + var diffy = e.Y - lastPoint.Y; + + origin.X += diffx; + origin.Y -= diffy; + + Invalidate(); + } + + lastPoint = e.Location; + } + + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (e.Button == MouseButtons.Middle) + { + ZoomToFit(); + Invalidate(); + } + } + + private void DrawAxis() + { + GL.Begin(PrimitiveType.Lines); + GL.Vertex2(origin.X, 0); + GL.Vertex2(origin.X, Height); + + GL.Vertex2(0, origin.Y); + GL.Vertex2(Width, origin.Y); + GL.End(); + } + + public void ZoomToPoint(double x, double y, float zoomFactor) + { + double x1 = ToRealX(x); + double y1 = ToRealY(y); + + origin.X -= (float)(x1 * zoomFactor - x1) * scale; + origin.Y -= (float)(y1 * zoomFactor - y1) * scale; + + scale *= zoomFactor; + } + + private void DrawPlate(Plate plate) + { + switch (plate.Quadrant) + { + case 1: + GL.Vertex2(0, 0); + GL.Vertex2(0, plate.Size.Width); + GL.Vertex2(plate.Size.Length, plate.Size.Width); + GL.Vertex2(plate.Size.Length, 0); + break; + + case 2: + GL.Vertex2(0, 0); + GL.Vertex2(0, plate.Size.Width); + GL.Vertex2(-plate.Size.Length, plate.Size.Width); + GL.Vertex2(-plate.Size.Length, 0); + break; + + case 3: + GL.Vertex2(0, 0); + GL.Vertex2(0, -plate.Size.Width); + GL.Vertex2(-plate.Size.Length, -plate.Size.Width); + GL.Vertex2(-plate.Size.Length, 0); + break; + + case 4: + GL.Vertex2(0, 0); + GL.Vertex2(0, -plate.Size.Width); + GL.Vertex2(plate.Size.Length, -plate.Size.Width); + GL.Vertex2(plate.Size.Length, 0); + break; + + default: + return; + } + } + + private void DrawProgram(libPep.Program pgm) + { + mode = pgm.Mode; + + foreach (var code in pgm) + { + switch (code.Type()) + { + case CodeType.Arc: + var arc = (Arc)code; + DrawArc(arc); + break; + + case CodeType.Line: + var line = (Line)code; + DrawLine(line); + break; + + case CodeType.SubProgramCall: + var tmpmode = mode; + var subpgm = (SubProgramCall)code; + + if (subpgm.Loop != null) + DrawProgram(subpgm.Loop); + + mode = tmpmode; + break; + } + } + } + + private void DrawLine(Line line) + { + var pt = line.EndPoint; + + if (mode == ProgrammingMode.Incremental) + pt += curpos; + + if (line.IsRapid) + { + if (DrawRapid) + { + GL.PushAttrib(AttribMask.EnableBit); + GL.Enable(EnableCap.LineStipple); + GL.LineStipple(2, 0xCCCC); + GL.Color3(RapidColor); + DrawLine(curpos, pt); + GL.Color3(GeometryColor); + GL.PopAttrib(); + } + } + else + DrawLine(curpos, pt); + + curpos = pt; + } + + private void DrawLine(Vector pt1, Vector pt2) + { + GL.Begin(PrimitiveType.Lines); + GL.Vertex2(pt1.X, pt1.Y); + GL.Vertex2(pt2.X, pt2.Y); + GL.End(); + } + + private void DrawArc(Arc arc) + { + var endpt = arc.EndPoint; + var center = arc.CenterPoint; + + if (mode == ProgrammingMode.Incremental) + { + endpt += curpos; + center += curpos; + } + + // start angle in radians + var startAngle = Math.Atan2( + curpos.Y - center.Y, + curpos.X - center.X); + + // end angle in radians + var endAngle = Math.Atan2( + endpt.Y - center.Y, + endpt.X - center.X); + + endAngle = NormalizeAngle(endAngle); + startAngle = NormalizeAngle(startAngle); + + if (arc.Rotation == RotationType.CCW && endAngle < startAngle) + endAngle += TwoPI; + else if (arc.Rotation == RotationType.CW && startAngle < endAngle) + startAngle += TwoPI; + + var dx = endpt.X - center.X; + var dy = endpt.Y - center.Y; + + var radius = Math.Sqrt(dx * dx + dy * dy); + + if (startAngle.IsEqualTo(endAngle)) + { + GL.End(); + GL.Begin(PrimitiveType.LineLoop); + DrawCircle(center, radius); + GL.End(); + GL.Begin(PrimitiveType.Polygon); + } + else + DrawArc(center, radius, endAngle, startAngle); + + curpos = endpt; + } + + private void DrawArc(Vector center, double radius, double startAngle, double endAngle) + { + GL.Begin(PrimitiveType.LineStrip); + + var angle = (endAngle - startAngle) / Resolution; + + for (int i = 0; i <= Resolution; i++) + { + GL.Vertex2( + Math.Cos(startAngle + angle * i) * radius + center.X, + Math.Sin(startAngle + angle * i) * radius + center.Y); + } + + GL.End(); + } + + private void DrawCircle(Vector center, double radius) + { + const float angle = (float)Math.PI * 2.0f; + const float increment = angle / Resolution; + + for (float i = 0; i <= angle; i += increment) + { + GL.Vertex2( + Math.Cos(i) * radius + center.X, + Math.Sin(i) * radius + center.Y); + } + + } + + private static double NormalizeAngle(double angle) + { + double r = angle % TwoPI; + return r < 0 ? TwoPI + r : r; + } + + private static void Swap(ref T a, ref T b) + { + T c = a; + a = b; + b = c; + } + + public void ZoomToFit() + { + if (Plate.Size.Width <= 0 || Plate.Size.Length <= 0) + return; + + float a = (this.Height - BorderWidth) / (float)Plate.Size.Width; + float b = (this.Width - BorderWidth) / (float)Plate.Size.Length; + + scale = a < b ? a : b; + + double px; + double py; + + switch (Plate.Quadrant) + { + case 1: + px = py = ToGui(0); + break; + + case 2: + px = ToGui(-Plate.Size.Length); + py = ToGui(0); + break; + + case 3: + px = ToGui(-Plate.Size.Length); + py = ToGui(-Plate.Size.Width); + break; + + case 4: + px = ToGui(0); + py = ToGui(-Plate.Size.Width); + break; + + default: + return; + } + + var pw = ToGui(Plate.Size.Length); + var ph = ToGui(Plate.Size.Width); + + origin.X = (float)((this.Width - pw) * 0.5f - px); + origin.Y = (float)((this.Height - ph) * 0.5f - py); + + Invalidate(); + } + + public float ToGui(double v) + { + return (float)v * scale; + } + + public double ToReal(double v) + { + return v / scale; + } + + public double ToRealX(double x) + { + return (x - origin.X) / scale; + } + + public double ToRealY(double y) + { + return (Height - y - origin.Y) / scale; + } + + public float ToGuiX(double x) + { + return scale * (float)x + origin.X; + } + + public float ToGuiY(double y) + { + return scale * (float)y + origin.Y; + } + } +} diff --git a/Source/OpenNest/Controls/NumericUpDown.cs b/Source/OpenNest/Controls/NumericUpDown.cs new file mode 100644 index 0000000..3518fb8 --- /dev/null +++ b/Source/OpenNest/Controls/NumericUpDown.cs @@ -0,0 +1,40 @@ +using System; + +namespace OpenNest.Controls +{ + public class NumericUpDown : System.Windows.Forms.NumericUpDown + { + private string suffix; + + + + public NumericUpDown() + { + suffix = string.Empty; + } + + public string Suffix + { + get { return suffix; } + set + { + suffix = value; + UpdateEditText(); + } + } + + protected override void OnEnter(EventArgs e) + { + base.OnEnter(e); + this.Select(0, Text.Length); + } + + protected override void UpdateEditText() + { + if (Suffix != null) + Text = Value.ToString("N" + DecimalPlaces) + Suffix; + else + base.UpdateEditText(); + } + } +} diff --git a/Source/OpenNest/Controls/PlateView.cs b/Source/OpenNest/Controls/PlateView.cs new file mode 100644 index 0000000..cbc29cf --- /dev/null +++ b/Source/OpenNest/Controls/PlateView.cs @@ -0,0 +1,880 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Windows.Forms; +using OpenNest.Actions; +using OpenNest.CNC; +using OpenNest.Collections; +using Action = OpenNest.Actions.Action; +using Timer = System.Timers.Timer; + +namespace OpenNest.Controls +{ + public class PlateView : DrawControl + { + private readonly Font programIdFont; + private readonly Timer redrawTimer; + + private string status; + private Plate plate; + private Action currentAction; + private List parts; + + public List SelectedParts; + public ReadOnlyCollection Parts; + + public event EventHandler PartAdded; + public event EventHandler PartRemoved; + public event EventHandler StatusChanged; + + public PlateView() + : this(ColorScheme.Default) + { + } + + public PlateView(ColorScheme colorScheme) + { + Plate = new Plate(60, 120); + programIdFont = new Font(DefaultFont, FontStyle.Bold | FontStyle.Underline); + origin = new PointF(); + parts = new List(); + Parts = new ReadOnlyCollection(parts); + SelectedParts = new List(); + + redrawTimer = new Timer() + { + AutoReset = false, + Enabled = true, + Interval = 50 + }; + redrawTimer.Elapsed += redrawTimer_Elapsed; + + SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.UserPaint, true); + + ViewScale = 1.0f; + RotateIncrementAngle = 10; + OffsetIncrementDistance = 10; + ColorScheme = colorScheme; + BackColor = colorScheme.BackgroundColor; + Cursor = Cursors.Cross; + AllowPan = true; + AllowSelect = true; + AllowZoom = true; + AllowDrop = true; + DrawOrigin = true; + DrawRapid = false; + DrawBounds = true; + FillParts = true; + SetAction(typeof(ActionSelect)); + + UpdateMatrix(); + } + + public ColorScheme ColorScheme { get; set; } + + public bool AllowZoom { get; set; } + + public bool AllowSelect { get; set; } + + public bool AllowPan { get; set; } + + public bool DrawOrigin { get; set; } + + public bool DrawRapid { get; set; } + + public bool DrawBounds { get; set; } + + public bool FillParts { get; set; } + + public double RotateIncrementAngle { get; set; } + + public double OffsetIncrementDistance { get; set; } + + public Plate Plate + { + get { return plate; } + set { SetPlate(value); } + } + + private void SetPlate(Plate p) + { + if (plate != null) + { + plate.PartAdded -= plate_PartAdded; + plate.PartRemoved -= plate_PartRemoved; + parts.Clear(); + SelectedParts.Clear(); + } + + plate = p; + plate.PartAdded += plate_PartAdded; + plate.PartRemoved += plate_PartRemoved; + + foreach (var part in plate.Parts) + parts.Add(LayoutPart.Create(part, this)); + + SetAction(typeof(ActionSelect)); + } + + public string Status + { + get { return status; } + protected set + { + status = value; + + if (StatusChanged != null) + StatusChanged.Invoke(this, new EventArgs()); + } + } + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + if (!Focused) Focus(); + } + + protected override void OnDragEnter(DragEventArgs drgevent) + { + if (drgevent.Data.GetData(typeof(Drawing)) != null) + drgevent.Effect = DragDropEffects.Copy; + } + + protected override void OnDragDrop(DragEventArgs drgevent) + { + var dwg = drgevent.Data.GetData(typeof(Drawing)) as Drawing; + + if (dwg == null) + return; + + var pt1 = PointToClient(new Point(drgevent.X, drgevent.Y)); + var pt2 = PointControlToWorld(pt1); + + AddPartFromDrawing(dwg, pt2); + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + var multiplier = Math.Abs(e.Delta / 120); + + if (SelectedParts.Count > 0 && ((ModifierKeys & Keys.Shift) == Keys.Shift)) + { + var increment = (ModifierKeys & Keys.Control) == Keys.Control + ? RotateIncrementAngle * 0.1 + : RotateIncrementAngle; + + var angle = Angle.ToRadians((e.Delta > 0 ? -increment : increment) * multiplier); + + RotateSelectedParts(angle); + } + else + { + if (AllowZoom) + { + if (e.Delta > 0) + ZoomToControlPoint(e.Location, (float)Math.Pow(ZoomInFactor, multiplier)); + else + ZoomToControlPoint(e.Location, (float)Math.Pow(ZoomOutFactor, multiplier)); + } + } + + Invalidate(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if (e.Button == MouseButtons.Middle) + { + if (AllowPan) + { + var diffx = e.X - lastPoint.X; + var diffy = e.Y - lastPoint.Y; + + origin.X += diffx; + origin.Y += diffy; + + Invalidate(); + } + } + else + { + LastPoint = CurrentPoint; + CurrentPoint = PointControlToWorld(e.Location); + } + + lastPoint = e.Location; + + base.OnMouseMove(e); + } + + protected override void OnMouseDoubleClick(MouseEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (e.Button == MouseButtons.Middle) + ZoomToFit(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Delete: + RemoveSelectedParts(); + break; + + default: + base.OnKeyDown(e); + break; + } + } + + protected override bool ProcessDialogKey(Keys keyData) + { + // Only handle TAB, RETURN, ESC, and ARROW KEYS here. + // All other keys can be handled in OnKeyDown method. + + switch (keyData) + { + case Keys.Escape: + if (currentAction.IsBusy()) + currentAction.CancelAction(); + else + SetAction(typeof(ActionSelect)); + break; + + case Keys.Left: + SelectedParts.ForEach(part => part.Offset(-OffsetIncrementDistance, 0)); + Invalidate(); + break; + + case Keys.X: + case Keys.Shift | Keys.Left: + PushSelected(PushDirection.Left); + break; + + case Keys.Shift | Keys.X: + case Keys.Shift | Keys.Right: + PushSelected(PushDirection.Right); + break; + + case Keys.Shift | Keys.Y: + case Keys.Shift | Keys.Up: + PushSelected(PushDirection.Up); + break; + + case Keys.Y: + case Keys.Shift | Keys.Down: + PushSelected(PushDirection.Down); + break; + + case Keys.Right: + SelectedParts.ForEach(part => part.Offset(OffsetIncrementDistance, 0)); + Invalidate(); + break; + + case Keys.Up: + SelectedParts.ForEach(part => part.Offset(0, OffsetIncrementDistance)); + Invalidate(); + break; + + case Keys.Down: + SelectedParts.ForEach(part => part.Offset(0, -OffsetIncrementDistance)); + Invalidate(); + break; + } + + return base.ProcessDialogKey(keyData); + } + + protected override void OnPaint(PaintEventArgs e) + { + e.Graphics.SmoothingMode = SmoothingMode.HighSpeed; + + if (DrawOrigin) + { + e.Graphics.DrawLine(ColorScheme.OriginPen, origin.X, 0, origin.X, Height); + e.Graphics.DrawLine(ColorScheme.OriginPen, 0, origin.Y, Width, origin.Y); + } + + e.Graphics.TranslateTransform(origin.X, origin.Y); + + DrawPlate(e.Graphics); + DrawParts(e.Graphics); + + base.OnPaint(e); + } + + protected override void OnHandleDestroyed(EventArgs e) + { + base.OnHandleDestroyed(e); + + if (currentAction != null) + { + currentAction.CancelAction(); + currentAction.DisconnectEvents(); + currentAction = null; + } + } + + public override void Refresh() + { + parts.ForEach(p => p.Update(this)); + Invalidate(); + } + + private void DrawPlate(Graphics g) + { + var plateRect = new RectangleF + { + Width = LengthWorldToGui(Plate.Size.Width), + Height = LengthWorldToGui(Plate.Size.Height) + }; + + var edgeSpacingRect = new RectangleF + { + Width = LengthWorldToGui(Plate.Size.Width - Plate.EdgeSpacing.Left - Plate.EdgeSpacing.Right), + Height = LengthWorldToGui(Plate.Size.Height - Plate.EdgeSpacing.Top - Plate.EdgeSpacing.Bottom) + }; + + switch (Plate.Quadrant) + { + case 1: + plateRect.Location = PointWorldToGraph(0, 0); + edgeSpacingRect.Location = PointWorldToGraph( + Plate.EdgeSpacing.Left, + Plate.EdgeSpacing.Bottom); + break; + + case 2: + plateRect.Location = PointWorldToGraph(-Plate.Size.Width, 0); + edgeSpacingRect.Location = PointWorldToGraph( + Plate.EdgeSpacing.Left - Plate.Size.Width, + Plate.EdgeSpacing.Bottom); + break; + + case 3: + plateRect.Location = PointWorldToGraph(-Plate.Size.Width, -Plate.Size.Height); + edgeSpacingRect.Location = PointWorldToGraph( + Plate.EdgeSpacing.Left - Plate.Size.Width, + Plate.EdgeSpacing.Bottom - Plate.Size.Height); + break; + + case 4: + plateRect.Location = PointWorldToGraph(0, -Plate.Size.Height); + edgeSpacingRect.Location = PointWorldToGraph( + Plate.EdgeSpacing.Left, + Plate.EdgeSpacing.Bottom - Plate.Size.Height); + break; + + default: + return; + } + + plateRect.Y -= plateRect.Height; + edgeSpacingRect.Y -= edgeSpacingRect.Height; + + g.FillRectangle(ColorScheme.LayoutFillBrush, plateRect); + + var viewBounds = new RectangleF(-origin.X, -origin.Y, Width, Height); + + if (!edgeSpacingRect.Contains(viewBounds)) + { + g.DrawRectangle(ColorScheme.EdgeSpacingPen, + edgeSpacingRect.X, + edgeSpacingRect.Y, + edgeSpacingRect.Width, + edgeSpacingRect.Height); + } + + g.DrawRectangle(ColorScheme.LayoutOutlinePen, + plateRect.X, + plateRect.Y, + plateRect.Width, + plateRect.Height); + } + + private void DrawParts(Graphics g) + { + var viewBounds = new RectangleF(-origin.X, -origin.Y, Width, Height); + + for (int i = 0; i < parts.Count; ++i) + { + var part = parts[i]; + + if (part.IsDirty) + part.Update(this); + + var path = part.Path; + var pathBounds = path.GetBounds(); + + if (!pathBounds.IntersectsWith(viewBounds)) + continue; + + part.Draw(g, (i + 1).ToString()); + } + + if (DrawBounds) + { + var bounds = SelectedParts.Select(p => p.BasePart).ToList().GetBoundingBox(); + DrawBox(g, bounds); + } + + if (DrawRapid) + DrawRapids(g); + } + + private void DrawRapids(Graphics g) + { + var pos = new Vector(0, 0); + + for (int i = 0; i < Plate.Parts.Count; ++i) + { + var part = Plate.Parts[i]; + var pgm = part.Program; + + DrawLine(g, pos, part.Location, ColorScheme.RapidPen); + pos = part.Location; + DrawRapids(g, pgm, ref pos); + } + } + + private void DrawRapids(Graphics g, Program pgm, ref Vector pos) + { + for (int i = 0; i < pgm.Length; ++i) + { + var code = pgm[i]; + + if (code.Type == CodeType.SubProgramCall) + { + var subpgm = (SubProgramCall)code; + var program = subpgm.Program; + + if (program != null) + DrawRapids(g, program, ref pos); + } + else + { + var motion = code as Motion; + + if (motion != null) + { + if (pgm.Mode == Mode.Incremental) + { + var endpt = motion.EndPoint + pos; + + if (code.Type == CodeType.RapidMove) + DrawLine(g, pos, endpt, ColorScheme.RapidPen); + pos = endpt; + } + else + { + if (code.Type == CodeType.RapidMove) + DrawLine(g, pos, motion.EndPoint, ColorScheme.RapidPen); + pos = motion.EndPoint; + } + } + } + } + } + + private void DrawLine(Graphics g, Vector pt1, Vector pt2, Pen pen) + { + var point1 = PointWorldToGraph(pt1); + var point2 = PointWorldToGraph(pt2); + + g.DrawLine(pen, point1, point2); + } + + private void DrawBox(Graphics g, Box box) + { + var rect = new RectangleF + { + Location = PointWorldToGraph(box.Location), + Width = LengthWorldToGui(box.Width), + Height = LengthWorldToGui(box.Height) + }; + + g.DrawRectangle(ColorScheme.BoundingBoxPen, rect.X, rect.Y - rect.Height, rect.Width, rect.Height); + } + + public LayoutPart GetPartAtControlPoint(Point pt) + { + var pt2 = PointControlToGraph(pt); + return GetPartAtGraphPoint(pt2); + } + + public LayoutPart GetPartAtGraphPoint(PointF pt) + { + for (int i = parts.Count - 1; i >= 0; --i) + { + if (parts[i].Path.IsVisible(pt)) + return parts[i]; + } + + return null; + } + + public LayoutPart GetPartAtPoint(Vector pt) + { + var pt2 = PointWorldToGraph(pt); + return GetPartAtGraphPoint(pt2); + } + + public IList GetPartsFromWindow(RectangleF rect, SelectionType selectionType) + { + var list = new List(); + + if (selectionType == SelectionType.Intersect) + { + for (int i = 0; i < parts.Count; ++i) + { + var part = parts[i]; + var path = part.Path; + var region = new Region(path); + + if (region.IsVisible(rect)) + list.Add(part); + + region.Dispose(); + } + } + else + { + for (int i = 0; i < parts.Count; ++i) + { + var part = parts[i]; + var path = part.Path; + var bounds = path.GetBounds(); + + if (rect.Contains(bounds)) + list.Add(part); + } + } + + return list; + } + + public void SetAction(Type type) + { + var action = Activator.CreateInstance(type, this) as Action; + + if (action == null) + return; + + if (currentAction != null) + { + currentAction.CancelAction(); + currentAction.DisconnectEvents(); + currentAction = null; + } + + currentAction = action; + + Status = GetDisplayName(type); + } + + public void SetAction(Type type, params object[] args) + { + if (currentAction != null) + { + currentAction.CancelAction(); + currentAction.DisconnectEvents(); + currentAction = null; + } + + Array.Resize(ref args, args.Length + 1); + + // shift all elements to the right + for (int i = args.Length - 2; i >= 0; i--) + args[i + 1] = args[i]; + + // set the first argument to this. + args[0] = this; + + var action = Activator.CreateInstance(type, args) as Action; + + if (action == null) + return; + + currentAction = action; + + Status = GetDisplayName(type); + } + + public void AlignSelected(AlignType alignType) + { + if (SelectedParts.Count == 0) + return; + + AlignSelected(alignType, SelectedParts[0]); + } + + public void AlignSelected(AlignType alignType, LayoutPart fixedPart) + { + switch (alignType) + { + case AlignType.Bottom: + Align.Bottom(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.Horizontally: + Align.Horizontally(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.Left: + Align.Left(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.Right: + Align.Right(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.Top: + Align.Top(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.Vertically: + Align.Vertically(fixedPart.BasePart, SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.EvenlySpaceHorizontally: + Align.EvenlyDistributeHorizontally(SelectedParts.Select(p => p.BasePart).ToList()); + break; + + case AlignType.EvenlySpaceVertically: + Align.EvenlyDistributeVertically(SelectedParts.Select(p => p.BasePart).ToList()); + break; + + default: + return; + } + + SelectedParts.ForEach(p => p.IsDirty = true); + Invalidate(); + } + + public void AddPartFromDrawing(Drawing dwg, Vector location) + { + var part = new Part(dwg, location); + + part.Offset( + part.Location.X - part.BoundingBox.Center.X, + part.Location.Y - part.BoundingBox.Center.Y); + + Plate.Parts.Add(part); + } + + public void RemoveSelectedParts() + { + foreach (var part in SelectedParts) + Plate.Parts.Remove(part.BasePart); + + DeselectAll(); + Invalidate(); + } + + private void redrawTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + Invalidate(); + } + + private void plate_PartAdded(object sender, PartAddedEventArgs e) + { + if (PartAdded != null) + PartAdded.Invoke(this, e); + + parts.Insert(e.Index, LayoutPart.Create(e.Part, this)); + redrawTimer.Start(); + } + + private void plate_PartRemoved(object sender, PartRemovedEventArgs e) + { + if (PartRemoved != null) + PartRemoved.Invoke(this, e); + + parts.RemoveAll(p => p.BasePart == e.Part); + } + + public void DeselectAll() + { + SelectedParts.ForEach(p => p.IsSelected = false); + SelectedParts.Clear(); + } + + public void SelectAll() + { + parts.ForEach(p => p.IsSelected = true); + SelectedParts.AddRange(parts); + } + + public override void ZoomToPoint(Vector pt, float zoomFactor, bool redraw = true) + { + base.ZoomToPoint(pt, zoomFactor, false); + + if (redraw) + Invalidate(); + } + + public override void ZoomToArea(double x, double y, double width, double height, bool redraw = true) + { + base.ZoomToArea(x, y, width, height, false); + + if (redraw) + Invalidate(); + } + + public virtual void ZoomToFit(bool redraw = true) + { + ZoomToArea(plate.BoundingBox(true), redraw); + } + + public virtual void ZoomToSelected(bool redraw = true) + { + ZoomToArea(SelectedParts.Select(p => p.BasePart).ToList().GetBoundingBox(), redraw); + } + + public virtual void ZoomToPlate(bool redraw = true) + { + ZoomToArea(plate.BoundingBox(false), redraw); + } + + public void PushSelected(PushDirection direction) + { + var boxes = new List(); + + foreach (var part in parts.Where(p => !p.IsSelected).ToList()) + { + if (!SelectedParts.Contains(part)) + boxes.Add(part.BoundingBox); + } + + var workArea = Plate.WorkArea(); + var distance = double.MaxValue; + var offset = new Vector(); + + switch (direction) + { + case PushDirection.Down: + SelectedParts.ForEach(part => + { + var d1 = Helper.ClosestDistanceDown(part.BoundingBox.Offset(Plate.PartSpacing), boxes); + + if (d1 < distance) + distance = d1; + + var d2 = part.Bottom - workArea.Bottom; + + if (d2 > 0 && d2 < distance) + distance = d2; + }); + offset.Y = -distance; + break; + + case PushDirection.Left: + SelectedParts.ForEach(part => + { + var d1 = Helper.ClosestDistanceLeft(part.BoundingBox.Offset(Plate.PartSpacing), boxes); + + if (d1 < distance) + distance = d1; + + var d2 = part.Left - workArea.Left; + + if (d2 > 0 && d2 < distance) + distance = d2; + }); + offset.X = -distance; + break; + + case PushDirection.Right: + SelectedParts.ForEach(part => + { + var d1 = Helper.ClosestDistanceRight(part.BoundingBox.Offset(Plate.PartSpacing), boxes); + + if (d1 < distance) + distance = d1; + + var d2 = workArea.Right - part.Right; + + if (d2 > 0 && d2 < distance) + distance = d2; + }); + offset.X = distance; + break; + + case PushDirection.Up: + SelectedParts.ForEach(part => + { + var d1 = Helper.ClosestDistanceUp(part.BoundingBox.Offset(Plate.PartSpacing), boxes); + + if (d1 < distance) + distance = d1; + + var d2 = workArea.Top - part.Top; + + if (d2 > 0 && d2 < distance) + distance = d2; + }); + offset.Y = distance; + break; + } + + if (distance < double.MaxValue) + { + SelectedParts.ForEach(p => p.Offset(offset)); + Invalidate(); + } + } + + private string GetDisplayName(Type type) + { + var attributes = type.GetCustomAttributes(true); + + foreach (var attr in attributes) + { + var displayNameAttr = attr as DisplayNameAttribute; + + if (displayNameAttr != null) + return displayNameAttr.DisplayName; + } + + return type.Name; + } + + public void RotateSelectedParts(double angle) + { + var pt1 = SelectedParts.Select(p => p.BasePart).ToList().GetBoundingBox().Location; + + for (int i = 0; i < SelectedParts.Count; ++i) + { + var part = SelectedParts[i]; + part.Rotate(angle); + } + + var pt2 = SelectedParts.Select(p => p.BasePart).ToList().GetBoundingBox().Location; + var diff = pt1 - pt2; + + for (int i = 0; i < SelectedParts.Count; ++i) + { + var part = SelectedParts[i]; + part.Offset(diff); + } + } + + protected override void UpdateMatrix() + { + base.UpdateMatrix(); + parts.ForEach(p => p.Update(this)); + } + } +} diff --git a/Source/OpenNest/Controls/QuadrantSelect.cs b/Source/OpenNest/Controls/QuadrantSelect.cs new file mode 100644 index 0000000..9c6c80a --- /dev/null +++ b/Source/OpenNest/Controls/QuadrantSelect.cs @@ -0,0 +1,82 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class QuadrantSelect : Control + { + private int quadrant; + + public QuadrantSelect() + { + } + + public int Quadrant + { + get { return quadrant; } + set { quadrant = value <= 4 && value > 0 ? value : 1; } + } + + protected override void OnPaint(PaintEventArgs e) + { + var midx = Width * 0.5f; + var midy = Height * 0.5f; + + RectangleF rect; + + switch (Quadrant) + { + case 1: + rect = new RectangleF(midx, 0, midx - 1, midy); + break; + + case 2: + rect = new RectangleF(0, 0, midx, midy); + break; + + case 3: + rect = new RectangleF(0, midy, midx, midy - 1); + break; + + case 4: + rect = new RectangleF(midx, midy, midx - 1, midy - 1); + break; + + default: + return; + } + + e.Graphics.DrawLine(Pens.Gray, midx, 0, midx, Height); + e.Graphics.DrawLine(Pens.Gray, 0, midy, Width, midy); + + e.Graphics.FillRectangle(Brushes.LightSkyBlue, rect); + e.Graphics.DrawRectangle(Pens.Blue, rect.X, rect.Y, rect.Width, rect.Height); + + e.Graphics.DrawString( + Quadrant.ToString(), + new Font(this.Font.FontFamily, 35, GraphicsUnit.Pixel), + Brushes.White, + rect, + new StringFormat() + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }); + } + + protected override void OnMouseClick(MouseEventArgs e) + { + base.OnMouseClick(e); + + var midx = Width / 2; + var midy = Height / 2; + + if (e.X < midx) + Quadrant = e.Y < midy ? 2 : 3; + else + Quadrant = e.Y < midy ? 1 : 4; + + Invalidate(); + } + } +} diff --git a/Source/OpenNest/Controls/VerticalLine.cs b/Source/OpenNest/Controls/VerticalLine.cs new file mode 100644 index 0000000..449fe70 --- /dev/null +++ b/Source/OpenNest/Controls/VerticalLine.cs @@ -0,0 +1,38 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Controls +{ + public class VerticalLine : Control + { + private readonly Pen lightPen; + private readonly Pen darkPen; + + public VerticalLine() + { + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.Selectable, false); + + lightPen = new Pen(ProfessionalColors.SeparatorLight); + darkPen = new Pen(ProfessionalColors.SeparatorDark); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + float midpoint = Width * 0.5f; + + e.Graphics.DrawLine(darkPen, midpoint, 0, midpoint, Height); + midpoint++; + e.Graphics.DrawLine(lightPen, midpoint, 0, midpoint, Height); + } + } +} diff --git a/Source/OpenNest/Document.cs b/Source/OpenNest/Document.cs new file mode 100644 index 0000000..6b4b256 --- /dev/null +++ b/Source/OpenNest/Document.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenNest.IO; + +namespace OpenNest +{ + public class Document + { + public Nest Nest { get; set; } + + public DateTime LastSaveDate { get; private set; } + + public string LastSavePath { get; private set; } + + public Units Units { get; set; } + + public void SaveAs(string path) + { + var name = Path.GetFileNameWithoutExtension(path); + Nest.Name = name; + LastSaveDate = DateTime.Now; + LastSavePath = path; + + var writer = new NestWriter(Nest); + writer.Write(path); + } + } +} diff --git a/Source/OpenNest/Forms/AutoNestForm.Designer.cs b/Source/OpenNest/Forms/AutoNestForm.Designer.cs new file mode 100644 index 0000000..14b87c5 --- /dev/null +++ b/Source/OpenNest/Forms/AutoNestForm.Designer.cs @@ -0,0 +1,127 @@ +namespace OpenNest.Forms +{ + partial class AutoNestForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.acceptButton = new System.Windows.Forms.Button(); + this.createNewPlatesAsNeededBox = new System.Windows.Forms.CheckBox(); + this.cancelButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // dataGridView1 + // + this.dataGridView1.AllowUserToDeleteRows = false; + this.dataGridView1.AllowUserToOrderColumns = true; + this.dataGridView1.AllowUserToResizeRows = false; + this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.dataGridView1.Location = new System.Drawing.Point(0, 0); + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.RowHeadersVisible = false; + this.dataGridView1.Size = new System.Drawing.Size(545, 385); + this.dataGridView1.TabIndex = 0; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.acceptButton); + this.bottomPanel1.Controls.Add(this.createNewPlatesAsNeededBox); + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 335); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(545, 50); + this.bottomPanel1.TabIndex = 9; + // + // acceptButton + // + this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.acceptButton.Location = new System.Drawing.Point(344, 9); + this.acceptButton.Margin = new System.Windows.Forms.Padding(4); + this.acceptButton.Name = "acceptButton"; + this.acceptButton.Size = new System.Drawing.Size(90, 28); + this.acceptButton.TabIndex = 6; + this.acceptButton.Text = "Accept"; + this.acceptButton.UseVisualStyleBackColor = true; + // + // createNewPlatesAsNeededBox + // + this.createNewPlatesAsNeededBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.createNewPlatesAsNeededBox.AutoSize = true; + this.createNewPlatesAsNeededBox.Location = new System.Drawing.Point(12, 15); + this.createNewPlatesAsNeededBox.Name = "createNewPlatesAsNeededBox"; + this.createNewPlatesAsNeededBox.Size = new System.Drawing.Size(202, 20); + this.createNewPlatesAsNeededBox.TabIndex = 8; + this.createNewPlatesAsNeededBox.Text = "Create new plates as needed"; + this.createNewPlatesAsNeededBox.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(442, 9); + this.cancelButton.Margin = new System.Windows.Forms.Padding(4); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 7; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // AutoNestForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(545, 385); + this.Controls.Add(this.bottomPanel1); + this.Controls.Add(this.dataGridView1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "AutoNestForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "AutoNest"; + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.bottomPanel1.ResumeLayout(false); + this.bottomPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.Button acceptButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.CheckBox createNewPlatesAsNeededBox; + private Controls.BottomPanel bottomPanel1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/AutoNestForm.cs b/Source/OpenNest/Forms/AutoNestForm.cs new file mode 100644 index 0000000..4018d54 --- /dev/null +++ b/Source/OpenNest/Forms/AutoNestForm.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class AutoNestForm : Form + { + public AutoNestForm(Nest nest) + { + InitializeComponent(); + LoadDrawings(nest); + + dataGridView1.DataError += dataGridView1_DataError; + } + + public bool AllowPlateCreation + { + get { return createNewPlatesAsNeededBox.Checked; } + set { createNewPlatesAsNeededBox.Checked = value; } + } + + private void LoadDrawings(Nest nest) + { + var items = new List(); + dataGridView1.Rows.Clear(); + + foreach (var drawing in nest.Drawings) + items.Add(GetDataGridViewItem(drawing)); + + dataGridView1.DataSource = items; + } + + public List GetNestItems() + { + var nestItems = new List(); + var gridItems = dataGridView1.DataSource as List; + + if (gridItems == null) + return nestItems; + + foreach (var gridItem in gridItems) + { + if (gridItem.Quantity < 1) + continue; + + var nestItem = new NestItem(); + nestItem.Drawing = gridItem.RefDrawing; + nestItem.Priority = gridItem.Priority; + nestItem.Quantity = gridItem.Quantity; + nestItem.RotationEnd = gridItem.RotationEnd; + nestItem.RotationStart = gridItem.RotationStart; + nestItem.StepAngle = gridItem.StepAngle; + + nestItems.Add(nestItem); + } + + return nestItems; + } + + private DataGridViewItem GetDataGridViewItem(Drawing dwg) + { + var item = new DataGridViewItem(); + item.RefDrawing = dwg; + item.Quantity = dwg.Quantity.Remaining > 0 ? dwg.Quantity.Remaining : 0; + item.Priority = dwg.Priority; + item.RotationStart = dwg.Constraints.StartAngle; + item.RotationEnd = dwg.Constraints.EndAngle; + item.StepAngle = dwg.Constraints.StepAngle; + + return item; + } + + private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) + { + MessageBox.Show("Invalid input. Expected input type is " + dataGridView1[e.ColumnIndex, e.RowIndex].ValueType.Name); + } + + private class DataGridViewItem + { + internal Drawing RefDrawing { get; set; } + + [ReadOnly(true)] + [DisplayName("Drawing Name")] + public string DrawingName + { + get { return RefDrawing.Name; } + set { RefDrawing.Name = value; } + } + + public int Quantity { get; set; } + + public int Priority { get; set; } + + [Browsable(false)] // hide until implemented + [DisplayName("Rotation Start")] + public double RotationStart { get; set; } + + [Browsable(false)] // hide until implemented + [DisplayName("Rotation End")] + public double RotationEnd { get; set; } + + [Browsable(false)] // hide until implemented + [DisplayName("Step Angle")] + public double StepAngle { get; set; } + } + } +} diff --git a/Source/OpenNest/Forms/AutoNestForm.resx b/Source/OpenNest/Forms/AutoNestForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/AutoNestForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/CadConverterForm.Designer.cs b/Source/OpenNest/Forms/CadConverterForm.Designer.cs new file mode 100644 index 0000000..6a9bd7c --- /dev/null +++ b/Source/OpenNest/Forms/CadConverterForm.Designer.cs @@ -0,0 +1,293 @@ +namespace OpenNest.Forms +{ + partial class CadConverterForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.splitContainer2 = new System.Windows.Forms.SplitContainer(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.checkedListBox1 = new System.Windows.Forms.CheckedListBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.checkedListBox2 = new System.Windows.Forms.CheckedListBox(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.checkedListBox3 = new System.Windows.Forms.CheckedListBox(); + this.entityView1 = new OpenNest.Controls.EntityView(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.cancelButton = new System.Windows.Forms.Button(); + this.acceptButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); + this.splitContainer2.Panel1.SuspendLayout(); + this.splitContainer2.Panel2.SuspendLayout(); + this.splitContainer2.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.dataGridView1); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.splitContainer2); + this.splitContainer1.Size = new System.Drawing.Size(928, 479); + this.splitContainer1.SplitterDistance = 225; + this.splitContainer1.TabIndex = 0; + // + // dataGridView1 + // + this.dataGridView1.AllowUserToAddRows = false; + this.dataGridView1.AllowUserToResizeRows = false; + this.dataGridView1.BackgroundColor = System.Drawing.Color.White; + this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(192))))); + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.Color.Black; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridView1.DefaultCellStyle = dataGridViewCellStyle1; + this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.dataGridView1.GridColor = System.Drawing.Color.Gainsboro; + this.dataGridView1.Location = new System.Drawing.Point(0, 0); + this.dataGridView1.MultiSelect = false; + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.RowHeadersVisible = false; + this.dataGridView1.RowTemplate.Height = 26; + this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dataGridView1.Size = new System.Drawing.Size(928, 225); + this.dataGridView1.TabIndex = 0; + this.dataGridView1.DataBindingComplete += new System.Windows.Forms.DataGridViewBindingCompleteEventHandler(this.dataGridView1_DataBindingComplete); + this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged); + // + // splitContainer2 + // + this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer2.Location = new System.Drawing.Point(0, 0); + this.splitContainer2.Name = "splitContainer2"; + // + // splitContainer2.Panel1 + // + this.splitContainer2.Panel1.Controls.Add(this.tabControl1); + // + // splitContainer2.Panel2 + // + this.splitContainer2.Panel2.Controls.Add(this.entityView1); + this.splitContainer2.Size = new System.Drawing.Size(928, 250); + this.splitContainer2.SplitterDistance = 309; + this.splitContainer2.TabIndex = 0; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 0); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(309, 250); + this.tabControl1.TabIndex = 0; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.checkedListBox1); + this.tabPage1.Location = new System.Drawing.Point(4, 25); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(301, 221); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Layers"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // checkedListBox1 + // + this.checkedListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.checkedListBox1.CheckOnClick = true; + this.checkedListBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.checkedListBox1.FormattingEnabled = true; + this.checkedListBox1.Location = new System.Drawing.Point(3, 3); + this.checkedListBox1.Name = "checkedListBox1"; + this.checkedListBox1.Size = new System.Drawing.Size(295, 215); + this.checkedListBox1.TabIndex = 0; + this.checkedListBox1.SelectedIndexChanged += new System.EventHandler(this.checkedListBox1_SelectedIndexChanged); + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.checkedListBox2); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(301, 294); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Colors"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // checkedListBox2 + // + this.checkedListBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.checkedListBox2.Dock = System.Windows.Forms.DockStyle.Fill; + this.checkedListBox2.FormattingEnabled = true; + this.checkedListBox2.Location = new System.Drawing.Point(3, 3); + this.checkedListBox2.Name = "checkedListBox2"; + this.checkedListBox2.Size = new System.Drawing.Size(295, 288); + this.checkedListBox2.TabIndex = 1; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.checkedListBox3); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(301, 294); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Line Types"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // checkedListBox3 + // + this.checkedListBox3.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.checkedListBox3.Dock = System.Windows.Forms.DockStyle.Fill; + this.checkedListBox3.FormattingEnabled = true; + this.checkedListBox3.Location = new System.Drawing.Point(3, 3); + this.checkedListBox3.Name = "checkedListBox3"; + this.checkedListBox3.Size = new System.Drawing.Size(295, 288); + this.checkedListBox3.TabIndex = 2; + // + // entityView1 + // + this.entityView1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(40)))), ((int)(((byte)(48))))); + this.entityView1.Cursor = System.Windows.Forms.Cursors.Cross; + this.entityView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.entityView1.Location = new System.Drawing.Point(0, 0); + this.entityView1.Name = "entityView1"; + this.entityView1.Size = new System.Drawing.Size(615, 250); + this.entityView1.TabIndex = 0; + this.entityView1.Text = "entityView1"; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Controls.Add(this.acceptButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 479); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(928, 50); + this.bottomPanel1.TabIndex = 1; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(826, 10); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // acceptButton + // + this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.acceptButton.Location = new System.Drawing.Point(730, 10); + this.acceptButton.Name = "acceptButton"; + this.acceptButton.Size = new System.Drawing.Size(90, 28); + this.acceptButton.TabIndex = 0; + this.acceptButton.Text = "Accept"; + this.acceptButton.UseVisualStyleBackColor = true; + // + // CadConverterForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(928, 529); + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MinimizeBox = false; + this.Name = "CadConverterForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "CAD Converter"; + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.splitContainer2.Panel1.ResumeLayout(false); + this.splitContainer2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); + this.splitContainer2.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.tabPage3.ResumeLayout(false); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button acceptButton; + private System.Windows.Forms.Button cancelButton; + private Controls.BottomPanel bottomPanel1; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.SplitContainer splitContainer2; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private Controls.EntityView entityView1; + private System.Windows.Forms.CheckedListBox checkedListBox1; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.CheckedListBox checkedListBox2; + private System.Windows.Forms.CheckedListBox checkedListBox3; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/CadConverterForm.cs b/Source/OpenNest/Forms/CadConverterForm.cs new file mode 100644 index 0000000..b731d11 --- /dev/null +++ b/Source/OpenNest/Forms/CadConverterForm.cs @@ -0,0 +1,388 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using OpenNest.CNC; +using OpenNest.Geometry; +using OpenNest.IO; +using OpenNest.Properties; +using System; +using System.Threading; + +namespace OpenNest.Forms +{ + public partial class CadConverterForm : Form + { + public CadConverterForm() + { + InitializeComponent(); + + Items = new BindingList(); + dataGridView1.DataSource = Items; + dataGridView1.DataError += dataGridView1_DataError; + } + + private BindingList Items { get; set; } + + private void SetRotation(Shape shape, RotationType rotation) + { + try + { + var dir = shape.ToPolygon(3).RotationDirection(); + + if (dir != RotationType.CW) + shape.Reverse(); + } + catch { } + } + + private void LoadItem(CadConverterItem item) + { + entityView1.Entities.Clear(); + entityView1.Entities.AddRange(item.Entities); + entityView1.ZoomToFit(); + + checkedListBox1.Items.Clear(); + + var layers = item.Entities + .Where(e => e.Layer != null) + .Select(e => e.Layer.Name) + .ToList() + .Distinct(); + + foreach (var layer in layers) + checkedListBox1.Items.Add(layer, true); + } + + private static Color GetNextColor() + { + //if (colorIndex >= Colors.Length) + // colorIndex = 0; + + //var color = Colors[colorIndex]; + + //colorIndex++; + + return new HSLColor(new Random().NextDouble() * 240, 240, 160); + + } + + public List GetDrawings() + { + var drawings = new List(); + + foreach (var item in Items) + { + var entities = item.Entities.Where(e => e.Layer.IsVisible).ToList(); + + if (entities.Count == 0) + continue; + + var drawing = new Drawing(item.Name); + drawing.Color = GetNextColor(); + drawing.Customer = item.Customer; + drawing.Source.Path = item.Path; + drawing.Quantity.Required = item.Quantity; + + var shape = new DefinedShape(entities); + + SetRotation(shape.Perimeter, RotationType.CW); + + foreach (var cutout in shape.Cutouts) + SetRotation(cutout, RotationType.CCW); + + entities = new List(); + entities.AddRange(shape.Perimeter.Entities); + + shape.Cutouts.ForEach(cutout => entities.AddRange(cutout.Entities)); + + var pgm = ConvertGeometry.ToProgram(entities); + var firstCode = pgm[0]; + + if (firstCode.Type == CodeType.RapidMove) + { + var rapid = (RapidMove)firstCode; + + drawing.Source.Offset = rapid.EndPoint; + + pgm.Offset(-rapid.EndPoint); + pgm.Codes.RemoveAt(0); + } + + drawing.Program = pgm; + drawings.Add(drawing); + + Thread.Sleep(20); + } + + return drawings; + } + + private CadConverterItem CurrentItem + { + get + { + return dataGridView1.SelectedRows.Count != 0 + ? Items[dataGridView1.SelectedRows[0].Index] + : null; + } + } + + public void AddFile(string file) + { + var importer = new DxfImporter(); + importer.SplinePrecision = Settings.Default.ImportSplinePrecision; + + var entities = new List(); + + if (!importer.GetGeometry(file, out entities)) + { + MessageBox.Show("Failed to import file \"" + file + "\""); + return; + } + + lock (Items) + { + Items.Add(new CadConverterItem + { + Name = Path.GetFileNameWithoutExtension(file), + Entities = entities, + Path = file, + Quantity = 1, + Customer = string.Empty + }); + } + } + + public void AddFiles(IEnumerable files) + { + Parallel.ForEach(files, AddFile); + } + + private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) + { + MessageBox.Show(e.Exception.Message); + } + + private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) + { + dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); + } + + private void dataGridView1_SelectionChanged(object sender, System.EventArgs e) + { + var currentItem = CurrentItem; + + if (currentItem != null) + LoadItem(currentItem); + } + + #region Colors + + private static Color[] Colors = new Color[] + { + Color.FromArgb(160, 255, 255), + Color.FromArgb(160, 255, 160), + Color.FromArgb(160, 160, 255), + Color.FromArgb(255, 255, 160), + Color.FromArgb(255, 160, 255), + Color.FromArgb(255, 160, 160), + + Color.FromArgb(200, 255, 255), + Color.FromArgb(200, 255, 200), + Color.FromArgb(200, 200, 255), + Color.FromArgb(255, 255, 200), + Color.FromArgb(255, 200, 255), + Color.FromArgb(255, 200, 200), + }; + + #endregion + + private void checkedListBox1_SelectedIndexChanged(object sender, System.EventArgs e) + { + var index = checkedListBox1.SelectedIndex; + var layerName = checkedListBox1.Items[index].ToString(); + var isVisible = checkedListBox1.CheckedItems.Contains(layerName); + + CurrentItem.Entities.ForEach(entity => + { + if (entity.Layer.Name == layerName) + entity.Layer.IsVisible = isVisible; + }); + + entityView1.Invalidate(); + } + } + + class CadConverterItem + { + public string Name { get; set; } + + public string Customer { get; set; } + + public int Quantity { get; set; } + + [ReadOnly(true)] + public string Path { get; set; } + + [Browsable(false)] + public List Entities { get; set; } + } + + public class RandomColorGenerator + { + private readonly Random random; + + public RandomColorGenerator() + { + random = new Random(); + } + + public Color GetNext() + { + var r = random.Next(255); + Thread.Sleep(20); + var g = random.Next(255); + Thread.Sleep(20); + var b = random.Next(255); + + return Color.FromArgb(r, g, b); + } + } + + public class HSLColor + { + // Private data members below are on scale 0-1 + // They are scaled for use externally based on scale + private double hue = 1.0; + private double saturation = 1.0; + private double luminosity = 1.0; + + private const double scale = 240.0; + + public double Hue + { + get { return hue * scale; } + set { hue = CheckRange(value / scale); } + } + public double Saturation + { + get { return saturation * scale; } + set { saturation = CheckRange(value / scale); } + } + public double Luminosity + { + get { return luminosity * scale; } + set { luminosity = CheckRange(value / scale); } + } + + private double CheckRange(double value) + { + if (value < 0.0) + value = 0.0; + else if (value > 1.0) + value = 1.0; + return value; + } + + public override string ToString() + { + return String.Format("H: {0:#0.##} S: {1:#0.##} L: {2:#0.##}", Hue, Saturation, Luminosity); + } + + public string ToRGBString() + { + Color color = (Color)this; + return String.Format("R: {0:#0.##} G: {1:#0.##} B: {2:#0.##}", color.R, color.G, color.B); + } + + #region Casts to/from System.Drawing.Color + public static implicit operator Color(HSLColor hslColor) + { + double r = 0, g = 0, b = 0; + if (hslColor.luminosity != 0) + { + if (hslColor.saturation == 0) + r = g = b = hslColor.luminosity; + else + { + double temp2 = GetTemp2(hslColor); + double temp1 = 2.0 * hslColor.luminosity - temp2; + + r = GetColorComponent(temp1, temp2, hslColor.hue + 1.0 / 3.0); + g = GetColorComponent(temp1, temp2, hslColor.hue); + b = GetColorComponent(temp1, temp2, hslColor.hue - 1.0 / 3.0); + } + } + return Color.FromArgb((int)(255 * r), (int)(255 * g), (int)(255 * b)); + } + + private static double GetColorComponent(double temp1, double temp2, double temp3) + { + temp3 = MoveIntoRange(temp3); + if (temp3 < 1.0 / 6.0) + return temp1 + (temp2 - temp1) * 6.0 * temp3; + else if (temp3 < 0.5) + return temp2; + else if (temp3 < 2.0 / 3.0) + return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0); + else + return temp1; + } + private static double MoveIntoRange(double temp3) + { + if (temp3 < 0.0) + temp3 += 1.0; + else if (temp3 > 1.0) + temp3 -= 1.0; + return temp3; + } + private static double GetTemp2(HSLColor hslColor) + { + double temp2; + if (hslColor.luminosity < 0.5) //<=?? + temp2 = hslColor.luminosity * (1.0 + hslColor.saturation); + else + temp2 = hslColor.luminosity + hslColor.saturation - (hslColor.luminosity * hslColor.saturation); + return temp2; + } + + public static implicit operator HSLColor(Color color) + { + HSLColor hslColor = new HSLColor(); + hslColor.hue = color.GetHue() / 360.0; // we store hue as 0-1 as opposed to 0-360 + hslColor.luminosity = color.GetBrightness(); + hslColor.saturation = color.GetSaturation(); + return hslColor; + } + #endregion + + public void SetRGB(int red, int green, int blue) + { + HSLColor hslColor = (HSLColor)Color.FromArgb(red, green, blue); + this.hue = hslColor.hue; + this.saturation = hslColor.saturation; + this.luminosity = hslColor.luminosity; + } + + public HSLColor() { } + public HSLColor(Color color) + { + SetRGB(color.R, color.G, color.B); + } + public HSLColor(int red, int green, int blue) + { + SetRGB(red, green, blue); + } + public HSLColor(double hue, double saturation, double luminosity) + { + this.Hue = hue; + this.Saturation = saturation; + this.Luminosity = luminosity; + } + + } +} diff --git a/Source/OpenNest/Forms/CadConverterForm.resx b/Source/OpenNest/Forms/CadConverterForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/CadConverterForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/CutParametersForm.Designer.cs b/Source/OpenNest/Forms/CutParametersForm.Designer.cs new file mode 100644 index 0000000..8a02808 --- /dev/null +++ b/Source/OpenNest/Forms/CutParametersForm.Designer.cs @@ -0,0 +1,241 @@ +namespace OpenNest.Forms +{ + partial class CutParametersForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.numericUpDown1 = new OpenNest.Controls.NumericUpDown(); + this.numericUpDown2 = new OpenNest.Controls.NumericUpDown(); + this.numericUpDown3 = new OpenNest.Controls.NumericUpDown(); + this.acceptButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown3)).BeginInit(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label6, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown1, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown2, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown3, 1, 2); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(5); + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(333, 120); + this.tableLayoutPanel1.TabIndex = 0; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 15); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(109, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Feedrate :"; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 51); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(109, 16); + this.label2.TabIndex = 0; + this.label2.Text = "Rapid Feedrate :"; + // + // label6 + // + this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(9, 88); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(109, 16); + this.label6.TabIndex = 0; + this.label6.Text = "Pierce Time :\r\n"; + // + // numericUpDown1 + // + this.numericUpDown1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown1.Location = new System.Drawing.Point(126, 12); + this.numericUpDown1.Margin = new System.Windows.Forms.Padding(4); + this.numericUpDown1.Maximum = new decimal(new int[] { + 9999, + 0, + 0, + 0}); + this.numericUpDown1.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numericUpDown1.Name = "numericUpDown1"; + this.numericUpDown1.Size = new System.Drawing.Size(198, 22); + this.numericUpDown1.Suffix = ""; + this.numericUpDown1.TabIndex = 0; + this.numericUpDown1.Value = new decimal(new int[] { + 60, + 0, + 0, + 0}); + // + // numericUpDown2 + // + this.numericUpDown2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown2.Location = new System.Drawing.Point(126, 48); + this.numericUpDown2.Margin = new System.Windows.Forms.Padding(4); + this.numericUpDown2.Maximum = new decimal(new int[] { + 9999, + 0, + 0, + 0}); + this.numericUpDown2.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numericUpDown2.Name = "numericUpDown2"; + this.numericUpDown2.Size = new System.Drawing.Size(198, 22); + this.numericUpDown2.Suffix = ""; + this.numericUpDown2.TabIndex = 1; + this.numericUpDown2.Value = new decimal(new int[] { + 1200, + 0, + 0, + 0}); + // + // numericUpDown3 + // + this.numericUpDown3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown3.DecimalPlaces = 1; + this.numericUpDown3.Location = new System.Drawing.Point(126, 85); + this.numericUpDown3.Margin = new System.Windows.Forms.Padding(4); + this.numericUpDown3.Name = "numericUpDown3"; + this.numericUpDown3.Size = new System.Drawing.Size(198, 22); + this.numericUpDown3.Suffix = " seconds"; + this.numericUpDown3.TabIndex = 2; + // + // acceptButton + // + this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.acceptButton.Location = new System.Drawing.Point(132, 11); + this.acceptButton.Margin = new System.Windows.Forms.Padding(4); + this.acceptButton.Name = "acceptButton"; + this.acceptButton.Size = new System.Drawing.Size(90, 28); + this.acceptButton.TabIndex = 8; + this.acceptButton.Text = "Accept"; + this.acceptButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(230, 11); + this.cancelButton.Margin = new System.Windows.Forms.Padding(4); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 9; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.acceptButton); + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 120); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(333, 50); + this.bottomPanel1.TabIndex = 3; + // + // CutParametersForm + // + this.AcceptButton = this.acceptButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(333, 170); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CutParametersForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Cut Parameters"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown3)).EndInit(); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label6; + private Controls.NumericUpDown numericUpDown1; + private Controls.NumericUpDown numericUpDown2; + private Controls.NumericUpDown numericUpDown3; + private Controls.BottomPanel bottomPanel1; + private System.Windows.Forms.Button acceptButton; + private System.Windows.Forms.Button cancelButton; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/CutParametersForm.cs b/Source/OpenNest/Forms/CutParametersForm.cs new file mode 100644 index 0000000..ae0561d --- /dev/null +++ b/Source/OpenNest/Forms/CutParametersForm.cs @@ -0,0 +1,64 @@ +using System; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class CutParametersForm : Form + { + private Units units; + + public CutParametersForm() + { + InitializeComponent(); + Units = OpenNest.Units.Inches; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + numericUpDown1.Value = Properties.Settings.Default.LastFeedrate; + numericUpDown2.Value = Properties.Settings.Default.LastRapidFeedrate; + numericUpDown3.Value = Properties.Settings.Default.LastPierceTime; + } + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + base.OnClosing(e); + + Properties.Settings.Default.LastFeedrate = numericUpDown1.Value; + Properties.Settings.Default.LastRapidFeedrate = numericUpDown2.Value; + Properties.Settings.Default.LastPierceTime = numericUpDown3.Value; + Properties.Settings.Default.Save(); + } + + public Units Units + { + get { return units; } + set + { + units = value; + SetUnits(value); + } + } + + private void SetUnits(Units units) + { + var suffix = UnitsHelper.GetShortTimeUnitPair(units); + + numericUpDown1.Suffix = " " + suffix; + numericUpDown2.Suffix = " " + suffix; + } + + public CutParameters GetCutParameters() + { + return new CutParameters() + { + Feedrate = (double)numericUpDown1.Value, + RapidTravelRate = (double)numericUpDown2.Value, + PierceTime = TimeSpan.FromSeconds((double)numericUpDown3.Value), + Units = units + }; + } + } +} diff --git a/Source/OpenNest/Forms/CutParametersForm.resx b/Source/OpenNest/Forms/CutParametersForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/CutParametersForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditDrawingForm.Designer.cs b/Source/OpenNest/Forms/EditDrawingForm.Designer.cs new file mode 100644 index 0000000..3d0c08b --- /dev/null +++ b/Source/OpenNest/Forms/EditDrawingForm.Designer.cs @@ -0,0 +1,268 @@ +namespace OpenNest.Forms +{ + partial class EditDrawingForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label1 = new System.Windows.Forms.Label(); + this.nameBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label2 = new System.Windows.Forms.Label(); + this.qtyBox = new OpenNest.Controls.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.customerBox = new System.Windows.Forms.TextBox(); + this.priorityBox = new OpenNest.Controls.NumericUpDown(); + this.applyButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.colorDialog1 = new System.Windows.Forms.ColorDialog(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.qtyBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.priorityBox)).BeginInit(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.BackColor = System.Drawing.Color.White; + this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pictureBox1.Location = new System.Drawing.Point(412, 12); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(289, 257); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 12); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(71, 16); + this.label1.TabIndex = 2; + this.label1.Text = "Name :"; + // + // nameBox + // + this.nameBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.nameBox.Location = new System.Drawing.Point(80, 9); + this.nameBox.Name = "nameBox"; + this.nameBox.Size = new System.Drawing.Size(311, 22); + this.nameBox.TabIndex = 3; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.nameBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.qtyBox, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.label4, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.customerBox, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.priorityBox, 1, 3); + this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(394, 160); + this.tableLayoutPanel1.TabIndex = 4; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 52); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(71, 16); + this.label2.TabIndex = 2; + this.label2.Text = "Quantity :"; + // + // qtyBox + // + this.qtyBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.qtyBox.Location = new System.Drawing.Point(80, 49); + this.qtyBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.qtyBox.Name = "qtyBox"; + this.qtyBox.Size = new System.Drawing.Size(311, 22); + this.qtyBox.Suffix = ""; + this.qtyBox.TabIndex = 4; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 92); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(71, 16); + this.label3.TabIndex = 2; + this.label3.Text = "Customer :"; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(3, 132); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(71, 16); + this.label4.TabIndex = 2; + this.label4.Text = "Priority :"; + // + // customerBox + // + this.customerBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.customerBox.Location = new System.Drawing.Point(80, 89); + this.customerBox.Name = "customerBox"; + this.customerBox.Size = new System.Drawing.Size(311, 22); + this.customerBox.TabIndex = 3; + // + // priorityBox + // + this.priorityBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.priorityBox.Location = new System.Drawing.Point(80, 129); + this.priorityBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.priorityBox.Name = "priorityBox"; + this.priorityBox.Size = new System.Drawing.Size(311, 22); + this.priorityBox.Suffix = ""; + this.priorityBox.TabIndex = 4; + // + // applyButton + // + this.applyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.applyButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.applyButton.Location = new System.Drawing.Point(515, 10); + this.applyButton.Name = "applyButton"; + this.applyButton.Size = new System.Drawing.Size(90, 28); + this.applyButton.TabIndex = 5; + this.applyButton.Text = "Done"; + this.applyButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(611, 10); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 6; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.applyButton); + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 298); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(713, 50); + this.bottomPanel1.TabIndex = 0; + // + // colorDialog1 + // + this.colorDialog1.AnyColor = true; + // + // linkLabel1 + // + this.linkLabel1.AutoSize = true; + this.linkLabel1.Location = new System.Drawing.Point(409, 272); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(90, 16); + this.linkLabel1.TabIndex = 5; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "Change Color"; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); + // + // EditDrawingForm + // + this.AcceptButton = this.applyButton; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(713, 348); + this.Controls.Add(this.linkLabel1); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditDrawingForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Drawing"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.qtyBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.priorityBox)).EndInit(); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.BottomPanel bottomPanel1; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox nameBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label2; + private Controls.NumericUpDown qtyBox; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox customerBox; + private Controls.NumericUpDown priorityBox; + private System.Windows.Forms.Button applyButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.ColorDialog colorDialog1; + private System.Windows.Forms.LinkLabel linkLabel1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditDrawingForm.cs b/Source/OpenNest/Forms/EditDrawingForm.cs new file mode 100644 index 0000000..cb8ce5a --- /dev/null +++ b/Source/OpenNest/Forms/EditDrawingForm.cs @@ -0,0 +1,84 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class EditDrawingForm : Form + { + private Drawing drawing; + + public EditDrawingForm() + { + InitializeComponent(); + } + + private string DrawingName + { + get { return nameBox.Text; } + set { nameBox.Text = value; } + } + + private string Customer + { + get { return customerBox.Text; } + set { customerBox.Text = value; } + } + + private int Quantity + { + get { return (int)qtyBox.Value; } + set { qtyBox.Value = value; } + } + + private int Priority + { + get { return (int)priorityBox.Value; } + set { priorityBox.Value = value; } + } + + private Image DrawingImage + { + get { return pictureBox1.Image; } + set { pictureBox1.Image = value; } + } + + private void UpdateImage() + { + var brush = new SolidBrush(colorDialog1.Color); + var pen = new Pen(ControlPaint.Dark(colorDialog1.Color)); + DrawingImage = drawing.Program.GetImage(pictureBox1.Size, pen, brush); + + pen.Dispose(); + brush.Dispose(); + } + + public void LoadDrawing(Drawing drawing) + { + this.drawing = drawing; + + colorDialog1.Color = drawing.Color; + DrawingName = drawing.Name; + Customer = drawing.Customer; + Quantity = drawing.Quantity.Required; + Priority = drawing.Priority; + UpdateImage(); + } + + public void SaveDrawing(Drawing drawing) + { + drawing.Name = DrawingName; + drawing.Customer = Customer; + drawing.Quantity.Required = Quantity; + drawing.Priority = Priority; + drawing.Color = colorDialog1.Color; + } + + private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + var result = colorDialog1.ShowDialog(); + + if (result == System.Windows.Forms.DialogResult.OK) + UpdateImage(); + } + } +} diff --git a/Source/OpenNest/Forms/EditDrawingForm.resx b/Source/OpenNest/Forms/EditDrawingForm.resx new file mode 100644 index 0000000..aa0ca0f --- /dev/null +++ b/Source/OpenNest/Forms/EditDrawingForm.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditNestForm.Designer.cs b/Source/OpenNest/Forms/EditNestForm.Designer.cs new file mode 100644 index 0000000..ad9014f --- /dev/null +++ b/Source/OpenNest/Forms/EditNestForm.Designer.cs @@ -0,0 +1,308 @@ +namespace OpenNest.Forms +{ + partial class EditNestForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditNestForm)); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.platesListView = new System.Windows.Forms.ListView(); + this.plateNumColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.sizeColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.qtyColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.toolStripButtonAddPlate = new System.Windows.Forms.ToolStripButton(); + this.toolStripButtonRemovePlate = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripButton1 = new System.Windows.Forms.ToolStripButton(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.drawingListBox1 = new OpenNest.Controls.DrawingListBox(); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripButton2 = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripButton3 = new System.Windows.Forms.ToolStripButton(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.tabControl1); + this.splitContainer.Size = new System.Drawing.Size(735, 396); + this.splitContainer.SplitterDistance = 241; + this.splitContainer.TabIndex = 8; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.ItemSize = new System.Drawing.Size(100, 22); + this.tabControl1.Location = new System.Drawing.Point(0, 0); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(241, 396); + this.tabControl1.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabControl1.TabIndex = 1; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.platesListView); + this.tabPage1.Controls.Add(this.toolStrip1); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(233, 370); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Plates"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // platesListView + // + this.platesListView.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.platesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.plateNumColumn, + this.sizeColumn, + this.qtyColumn}); + this.platesListView.Dock = System.Windows.Forms.DockStyle.Fill; + this.platesListView.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.platesListView.FullRowSelect = true; + this.platesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.platesListView.HideSelection = false; + this.platesListView.Location = new System.Drawing.Point(3, 34); + this.platesListView.MultiSelect = false; + this.platesListView.Name = "platesListView"; + this.platesListView.Size = new System.Drawing.Size(227, 333); + this.platesListView.TabIndex = 1; + this.platesListView.UseCompatibleStateImageBehavior = false; + this.platesListView.View = System.Windows.Forms.View.Details; + this.platesListView.DoubleClick += new System.EventHandler(this.EditSelectedPlate_Click); + // + // plateNumColumn + // + this.plateNumColumn.Text = ""; + this.plateNumColumn.Width = 30; + // + // sizeColumn + // + this.sizeColumn.Text = "Size"; + this.sizeColumn.Width = 80; + // + // qtyColumn + // + this.qtyColumn.Text = "Qty"; + // + // toolStrip1 + // + this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButtonAddPlate, + this.toolStripButtonRemovePlate, + this.toolStripSeparator2, + this.toolStripButton1}); + this.toolStrip1.Location = new System.Drawing.Point(3, 3); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(227, 31); + this.toolStrip1.TabIndex = 2; + this.toolStrip1.Text = "toolStrip1"; + // + // toolStripButtonAddPlate + // + this.toolStripButtonAddPlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButtonAddPlate.Image = global::OpenNest.Properties.Resources.add; + this.toolStripButtonAddPlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.toolStripButtonAddPlate.Name = "toolStripButtonAddPlate"; + this.toolStripButtonAddPlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.toolStripButtonAddPlate.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.toolStripButtonAddPlate.Size = new System.Drawing.Size(38, 28); + this.toolStripButtonAddPlate.Text = "Add Plate"; + this.toolStripButtonAddPlate.Click += new System.EventHandler(this.AddPlate_Click); + // + // toolStripButtonRemovePlate + // + this.toolStripButtonRemovePlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButtonRemovePlate.Image = global::OpenNest.Properties.Resources.remove; + this.toolStripButtonRemovePlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.toolStripButtonRemovePlate.Name = "toolStripButtonRemovePlate"; + this.toolStripButtonRemovePlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.toolStripButtonRemovePlate.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.toolStripButtonRemovePlate.Size = new System.Drawing.Size(38, 28); + this.toolStripButtonRemovePlate.Text = "Remove Selected Plate"; + this.toolStripButtonRemovePlate.Click += new System.EventHandler(this.RemoveSelectedPlate_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(6, 31); + // + // toolStripButton1 + // + this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButton1.Image = global::OpenNest.Properties.Resources.clock; + this.toolStripButton1.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton1.Name = "toolStripButton1"; + this.toolStripButton1.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.toolStripButton1.Size = new System.Drawing.Size(38, 28); + this.toolStripButton1.Text = "Calculate Cut Time"; + this.toolStripButton1.Click += new System.EventHandler(this.CalculateSelectedPlateCutTime_Click); + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.drawingListBox1); + this.tabPage2.Controls.Add(this.toolStrip2); + this.tabPage2.Location = new System.Drawing.Point(4, 26); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(233, 366); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Drawings"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // drawingListBox1 + // + this.drawingListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.drawingListBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.drawingListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; + this.drawingListBox1.FormattingEnabled = true; + this.drawingListBox1.ItemHeight = 85; + this.drawingListBox1.Location = new System.Drawing.Point(3, 34); + this.drawingListBox1.Name = "drawingListBox1"; + this.drawingListBox1.Size = new System.Drawing.Size(227, 329); + this.drawingListBox1.TabIndex = 1; + this.drawingListBox1.Units = OpenNest.Units.Inches; + this.drawingListBox1.Click += new System.EventHandler(this.drawingListBox1_Click); + this.drawingListBox1.DoubleClick += new System.EventHandler(this.drawingListBox1_DoubleClick); + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.ImageScalingSize = new System.Drawing.Size(20, 20); + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripButton2, + this.toolStripSeparator1, + this.toolStripButton3}); + this.toolStrip2.Location = new System.Drawing.Point(3, 3); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(227, 31); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripButton2 + // + this.toolStripButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButton2.Image = global::OpenNest.Properties.Resources.import; + this.toolStripButton2.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.toolStripButton2.Name = "toolStripButton2"; + this.toolStripButton2.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.toolStripButton2.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.toolStripButton2.Size = new System.Drawing.Size(38, 28); + this.toolStripButton2.Text = "Import Drawings"; + this.toolStripButton2.Click += new System.EventHandler(this.ImportDrawings_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 31); + // + // toolStripButton3 + // + this.toolStripButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.toolStripButton3.Image = global::OpenNest.Properties.Resources.clear; + this.toolStripButton3.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.toolStripButton3.Name = "toolStripButton3"; + this.toolStripButton3.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.toolStripButton3.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.toolStripButton3.Size = new System.Drawing.Size(38, 28); + this.toolStripButton3.Text = "Cleanup unused Drawings"; + this.toolStripButton3.Click += new System.EventHandler(this.CleanUnusedDrawings_Click); + // + // EditNestForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(735, 396); + this.Controls.Add(this.splitContainer); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(200, 198); + this.Name = "EditNestForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "EditForm"; + this.splitContainer.Panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.ListView platesListView; + private System.Windows.Forms.ColumnHeader sizeColumn; + private System.Windows.Forms.ColumnHeader qtyColumn; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.ToolStripButton toolStripButtonAddPlate; + private System.Windows.Forms.ToolStripButton toolStripButtonRemovePlate; + private System.Windows.Forms.ToolStripButton toolStripButton1; + private System.Windows.Forms.TabPage tabPage2; + private Controls.DrawingListBox drawingListBox1; + private System.Windows.Forms.ColumnHeader plateNumColumn; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStrip toolStrip2; + private System.Windows.Forms.ToolStripButton toolStripButton2; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripButton toolStripButton3; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditNestForm.cs b/Source/OpenNest/Forms/EditNestForm.cs new file mode 100644 index 0000000..f6233b5 --- /dev/null +++ b/Source/OpenNest/Forms/EditNestForm.cs @@ -0,0 +1,688 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using OpenNest.Actions; +using OpenNest.Collections; +using OpenNest.Controls; +using OpenNest.IO; +using OpenNest.Properties; +using Timer = System.Timers.Timer; + +namespace OpenNest.Forms +{ + public partial class EditNestForm : Form + { + public event EventHandler PlateChanged; + + public readonly Nest Nest; + public readonly PlateView PlateView; + + private readonly Timer updateDrawingListTimer; + + /// + /// Used to distinguish between single/double click on drawing within drawinglistbox. + /// If double click, this is set to false so the single click action won't be triggered. + /// + private bool addPart; + + private EditNestForm() + { + PlateView = new PlateView(); + PlateView.Enter += PlateView_Enter; + PlateView.PartAdded += PlateView_PartAdded; + PlateView.PartRemoved += PlateView_PartRemoved; + PlateView.Dock = DockStyle.Fill; + + InitializeComponent(); + + splitContainer.Panel2.Controls.Add(PlateView); + + var renderer = new ToolStripRenderer(ToolbarTheme.Toolbar); + toolStrip1.Renderer = renderer; + toolStrip2.Renderer = renderer; + + platesListView.SelectedIndexChanged += (sender, e) => + { + if (platesListView.SelectedIndices.Count == 0) + return; + + CurrentPlateIndex = platesListView.SelectedIndices[0]; + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + PlateView.ZoomToFit(); + FirePlateChanged(false); + }; + } + + public EditNestForm(Nest nest) + : this() + { + updateDrawingListTimer = new Timer() + { + AutoReset = false, + Enabled = true, + Interval = 50 + }; + updateDrawingListTimer.Elapsed += drawingListUpdateTimer_Elapsed; + + Nest = nest; + Nest.Plates.PlateAdded += Plates_PlateAdded; + Nest.Plates.PlateRemoved += Plates_PlateRemoved; + + if (Nest.Plates.Count == 0) + Nest.CreatePlate(); + + UpdatePlateList(); + UpdateDrawingList(); + + LoadFirstPlate(); + + Text = Nest.Name; + drawingListBox1.Units = Nest.Units; + } + + public string LastSavePath { get; private set; } + + public DateTime LastSaveDate { get; private set; } + + public int CurrentPlateIndex { get; private set; } + + public int PlateCount + { + get { return Nest.Plates.Count; } + } + + public void LoadFirstPlate() + { + if (Nest.Plates.Count > 0) + { + CurrentPlateIndex = 0; + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + PlateView.ZoomToFit(); + FirePlateChanged(); + } + } + + public void LoadLastPlate() + { + if (Nest.Plates.Count > 0) + { + CurrentPlateIndex = Nest.Plates.Count - 1; + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + PlateView.ZoomToFit(); + FirePlateChanged(); + } + } + + public bool LoadNextPlate() + { + if (CurrentPlateIndex + 1 >= Nest.Plates.Count) + return false; + + CurrentPlateIndex++; + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + PlateView.ZoomToFit(); + FirePlateChanged(); + + return true; + } + + public bool LoadPreviousPlate() + { + if (Nest.Plates.Count == 0 || CurrentPlateIndex - 1 < 0) + return false; + + CurrentPlateIndex--; + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + PlateView.ZoomToFit(); + FirePlateChanged(); + + return true; + } + + public bool IsFirstPlate() + { + return (Nest.Plates.Count == 0 || (CurrentPlateIndex - 1) < 0); + } + + public bool IsLastPlate() + { + return CurrentPlateIndex + 1 >= Nest.Plates.Count; + } + + public void UpdatePlateList() + { + platesListView.Items.Clear(); + + var items = new ListViewItem[Nest.Plates.Count]; + + for (int i = 0; i < items.Length; ++i) + { + var plate = Nest.Plates[i]; + var item = GetListViewItem(plate, i + 1); + items[i] = item; + } + + platesListView.Items.AddRange(items); + } + + public void UpdateDrawingList() + { + drawingListBox1.Items.Clear(); + + foreach (var dwg in Nest.Drawings.OrderBy(d => d.Name).ToList()) + drawingListBox1.Items.Add(dwg); + } + + public void Save() + { + if (File.Exists(LastSavePath)) + SaveAs(LastSavePath); + else + SaveAs(); + } + + public void SaveAs() + { + var dlg = new SaveFileDialog(); + dlg.Filter = "Nest Files|*.zip|Template File|*.nstdot"; + dlg.FileName = Nest.Name; + + if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + if (dlg.FilterIndex == 2) + SaveTemplate(dlg.FileName); + else + SaveAs(dlg.FileName); + } + } + + public void SaveAs(string path) + { + var name = Path.GetFileNameWithoutExtension(path); + Nest.Name = name; + Text = name; + LastSaveDate = DateTime.Now; + LastSavePath = path; + + var writer = new NestWriter(Nest); + writer.Write(path); + } + + public void SaveTemplate(string path) + { + var nst = new Nest(); + nst.Name = Path.GetFileNameWithoutExtension(path); + nst.PlateDefaults = Nest.PlateDefaults; + nst.Units = Nest.Units; + + var writer = new NestWriter(nst); + writer.Write(path); + } + + public void Import() + { + var dlg = new OpenFileDialog(); + dlg.Multiselect = true; + dlg.Filter = "DXF Files (*.dxf) | *.dxf"; + + if (dlg.ShowDialog() != DialogResult.OK) + return; + + var converter = new CadConverterForm(); + converter.AddFiles(dlg.FileNames); + + var result = converter.ShowDialog(); + + if (result != DialogResult.OK) + return; + + var drawings = converter.GetDrawings(); + drawings.ForEach(d => Nest.Drawings.Add(d)); + + UpdateDrawingList(); + tabControl1.SelectedIndex = 1; + } + + public bool Export() + { + var dlg = new SaveFileDialog(); + dlg.Filter = "DXF file (*.dxf)|*.dxf|" + + "Image as displayed (*.jpg)|*.jpg|" + + "Locations and rotations (*.txt)|*.txt"; + + dlg.FileName = string.Format("{0}-P{1}", Nest.Name, CurrentPlateIndex + 1); + dlg.AddExtension = true; + dlg.DefaultExt = "."; + + if (dlg.ShowDialog() == DialogResult.OK) + { + if (dlg.FilterIndex == 1) + { + var exporter = new DxfExporter(); + var success = exporter.ExportPlate(PlateView.Plate, dlg.FileName); + return success; + } + else if (dlg.FilterIndex == 2) + { + try + { + var img = new Bitmap(PlateView.Width, PlateView.Height); + PlateView.DrawToBitmap(img, new Rectangle(0, 0, PlateView.Width, PlateView.Height)); + img.Save(dlg.FileName); + } + catch { } + + return true; + } + else if (dlg.FilterIndex == 3) + { + StreamWriter writer = null; + + try + { + writer = new StreamWriter(dlg.FileName); + + foreach (var part in PlateView.Plate.Parts) + { + var pt = part.BaseDrawing.Source.Offset.Rotate(part.Rotation); + + writer.WriteLine("{0}|{1},{2}|{3}", + part.BaseDrawing.Source.Path, + Math.Round(part.Location.X - pt.X, 8), + Math.Round(part.Location.Y - pt.Y, 8), + Angle.ToDegrees(part.Rotation)); + } + } + catch { } + finally + { + if (writer != null) + writer.Dispose(); + } + } + } + + return false; + } + + public void ExportAll() + { + LoadFirstPlate(); + + do + { + if (!Export()) return; + } + while (LoadNextPlate()); + } + + public void RotateCw() + { + PlateView.Plate.Rotate90(RotationType.CW); + PlateView.ZoomToFit(); + } + + public void RotateCcw() + { + PlateView.Plate.Rotate90(RotationType.CCW); + PlateView.ZoomToFit(); + } + + public void Rotate180() + { + PlateView.Plate.Rotate180(); + PlateView.ZoomToFit(); + } + + public void ShowNestInfoEditor() + { + var form = new EditNestInfoForm(); + form.LoadNestInfo(Nest); + + if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + form.SaveNestInfo(Nest); + drawingListBox1.Units = Nest.Units; + drawingListBox1.Invalidate(); + } + } + + public void ResizePlateToFitParts() + { + PlateView.Plate.AutoSize(Settings.Default.AutoSizePlateFactor); + PlateView.ZoomToPlate(); + PlateView.Refresh(); + UpdatePlateList(); + } + + public void SelectAllParts() + { + PlateView.SelectAll(); + PlateView.Invalidate(); + } + + public void ToggleRapid() + { + PlateView.DrawRapid = !PlateView.DrawRapid; + PlateView.Invalidate(); + } + + public void ToggleDrawBounds() + { + PlateView.DrawBounds = !PlateView.DrawBounds; + PlateView.Invalidate(); + } + + public void ToggleFillParts() + { + PlateView.FillParts = !PlateView.FillParts; + PlateView.Invalidate(); + } + + public void SetCurrentPlateAsNestDefault() + { + Nest.PlateDefaults.SetFromExisting(PlateView.Plate); + } + + public void EditPlate() + { + var form = new EditPlateForm(PlateView.Plate); + form.Units = Nest.Units; + + if (form.ShowDialog() == DialogResult.OK) + { + PlateView.Invalidate(); + FirePlateChanged(false); + UpdatePlateList(); + } + + Nest.UpdateDrawingQuantities(); + drawingListBox1.Refresh(); + + if (PlateView.Plate.Parts.Count == 0) + Nest.PlateDefaults.SetFromExisting(PlateView.Plate); + } + + /// + /// Opens the current plate as a DXF file by the default program set in Windows. + /// + public void OpenCurrentPlate() + { + var plate = PlateView.Plate; + var name = string.Format("{0}-P{1}.dxf", Nest.Name, CurrentPlateIndex + 1); + var path = Path.Combine(Path.GetTempPath(), name); + var exporter = new DxfExporter(); + exporter.ExportPlate(plate, path); + + Process.Start(path); + } + + public void RemoveCurrentPlate() + { + if (Nest.Plates.Count < 2) + return; + + Nest.Plates.RemoveAt(CurrentPlateIndex); + } + + public void AutoSequenceCurrentPlate() + { + var seq = new SequenceByNearest(); + var parts = seq.SequenceParts(PlateView.Plate.Parts); + + PlateView.Plate.Parts.Clear(); + PlateView.Plate.Parts.AddRange(parts); + } + + public void AutoSequenceAllPlates() + { + var seq = new SequenceByNearest(); + + foreach (var plate in Nest.Plates) + { + var parts = seq.SequenceParts(plate.Parts); + plate.Parts.Clear(); + plate.Parts.AddRange(parts); + } + + PlateView.Invalidate(); + } + + public void CalculateCurrentPlateCutTime() + { + var cutParamsForm = new CutParametersForm(); + cutParamsForm.Units = Nest.Units; + + if (cutParamsForm.ShowDialog() == DialogResult.OK) + { + var cutparams = cutParamsForm.GetCutParameters(); + var info = Timing.GetTimingInfo(PlateView.Plate); + var time = Timing.CalculateTime(info, cutparams); + + var timingForm = new TimingForm(); + timingForm.Units = Nest.Units; + timingForm.SetCutDistance(info.CutDistance); + timingForm.SetCutTime(time); + timingForm.SetIntersectionCount(info.IntersectionCount); + timingForm.SetPierceCount(info.PierceCount); + timingForm.SetRapidDistance(info.TravelDistance); + timingForm.SetCutParameters(cutparams); + + timingForm.ShowDialog(); + } + } + + public void CalculateNestCutTime() + { + var cutParamsForm = new CutParametersForm(); + cutParamsForm.Units = Nest.Units; + + if (cutParamsForm.ShowDialog() == DialogResult.OK) + { + var cutparams = cutParamsForm.GetCutParameters(); + var info = Timing.GetTimingInfo(Nest); + var time = Timing.CalculateTime(info, cutparams); + + var timingForm = new TimingForm(); + timingForm.Units = Nest.Units; + timingForm.SetCutDistance(info.CutDistance); + timingForm.SetCutTime(time); + timingForm.SetIntersectionCount(info.IntersectionCount); + timingForm.SetPierceCount(info.PierceCount); + timingForm.SetRapidDistance(info.TravelDistance); + timingForm.SetCutParameters(cutparams); + + timingForm.ShowDialog(); + } + } + + private void FirePlateChanged(bool updateListView = true) + { + if (updateListView) + platesListView.Items[CurrentPlateIndex].Selected = true; + + if (PlateChanged != null) + PlateChanged.Invoke(this, EventArgs.Empty); + } + + #region Overrides + + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + PlateView.Invalidate(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + Icon = Icon.Clone() as Icon; + PlateView.DrawBounds = Settings.Default.PlateViewDrawBounds; + PlateView.DrawRapid = Settings.Default.PlateViewDrawRapid; + splitContainer.SplitterDistance = Settings.Default.SplitterDistance; + } + + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + + Settings.Default.PlateViewDrawBounds = PlateView.DrawBounds; + Settings.Default.PlateViewDrawRapid = PlateView.DrawRapid; + Settings.Default.SplitterDistance = splitContainer.SplitterDistance; + Settings.Default.Save(); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + PlateView.Invalidate(); + } + + #endregion + + #region Plates/Drawings Panel Events + + private void AddPlate_Click(object sender, EventArgs e) + { + Nest.CreatePlate(); + } + + private void EditSelectedPlate_Click(object sender, EventArgs e) + { + if (platesListView.SelectedIndices.Count != 0) + EditPlate(); + } + + private void RemoveSelectedPlate_Click(object sender, EventArgs e) + { + RemoveCurrentPlate(); + } + + private void CalculateSelectedPlateCutTime_Click(object sender, EventArgs e) + { + CalculateCurrentPlateCutTime(); + } + + private void ImportDrawings_Click(object sender, EventArgs e) + { + Import(); + } + + private void CleanUnusedDrawings_Click(object sender, EventArgs e) + { + var result = MessageBox.Show( + "Remove unused drawings?", + "Clean Drawings", + MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + + if (result == DialogResult.Yes) + { + Nest.Drawings.RemoveWhere(d => d.Quantity.Nested == 0); + UpdateDrawingList(); + } + } + + #endregion + + #region Plate Collection Events + + private void Plates_PlateRemoved(object sender, PlateRemovedEventArgs e) + { + if (Nest.Plates.Count <= CurrentPlateIndex) + LoadLastPlate(); + else + PlateView.Plate = Nest.Plates[CurrentPlateIndex]; + + UpdatePlateList(); + PlateView.ZoomToFit(); + } + + private void Plates_PlateAdded(object sender, PlateAddedEventArgs e) + { + tabControl1.SelectedIndex = 0; + UpdatePlateList(); + LoadLastPlate(); + PlateView.ZoomToFit(); + } + + #endregion + + private static ListViewItem GetListViewItem(Plate plate, int id) + { + var item = new ListViewItem(); + item.Text = id.ToString(); + item.SubItems.Add(plate.Size.ToString()); + item.SubItems.Add(plate.Quantity.ToString()); + return item; + } + + private void PlateView_PartRemoved(object sender, PartRemovedEventArgs e) + { + updateDrawingListTimer.Stop(); + updateDrawingListTimer.Start(); + } + + private void PlateView_PartAdded(object sender, PartAddedEventArgs e) + { + updateDrawingListTimer.Stop(); + updateDrawingListTimer.Start(); + } + + private void drawingListUpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + drawingListBox1.Invoke(new MethodInvoker(() => + { + drawingListBox1.Refresh(); + })); + } + + private void drawingListBox1_DoubleClick(object sender, EventArgs e) + { + addPart = false; + + var drawing = drawingListBox1.SelectedItem as Drawing; + + if (drawing == null) + return; + + var form = new EditDrawingForm(); + form.LoadDrawing(drawing); + + if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + form.SaveDrawing(drawing); + + foreach (var part in PlateView.Parts) + part.Update(); + + PlateView.Invalidate(); + } + } + + private void drawingListBox1_Click(object sender, EventArgs e) + { + addPart = true; + } + + private void PlateView_Enter(object sender, EventArgs e) + { + if (!addPart) + return; + + var drawing = drawingListBox1.SelectedItem as Drawing; + + if (drawing == null) + return; + + PlateView.SetAction(typeof(ActionAddPart), drawing); + + addPart = false; + } + } +} diff --git a/Source/OpenNest/Forms/EditNestForm.resx b/Source/OpenNest/Forms/EditNestForm.resx new file mode 100644 index 0000000..3739849 --- /dev/null +++ b/Source/OpenNest/Forms/EditNestForm.resx @@ -0,0 +1,706 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 122, 17 + + + + + AAABAAUAQEAAAAEAIAAoQgAAVgAAADAwAAABACAAqCUAAH5CAAAgIAAAAQAgAKgQAAAmaAAAGBgAAAEA + IACICQAAzngAABAQAAABACAAaAQAAFaCAAAoAAAAQAAAAIAAAAABACAAAAAAAABCAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAACAAAACQAAAAwAAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAA + AA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAA + AA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAA + AA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAMAAAACAAAAAEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAADgAAABUAAAAZAAAAGgAAABsAAAAbAAAAGwAA + ABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAA + ABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAA + ABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAaAAAAGQAA + ABQAAAANAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAADwAAABgAAAAgAAAAJgAA + ACYAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAA + ACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAA + ACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAA + ACkAAAApAAAAJwAAACYAAAAfAAAAFwAAAA0AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAA + ABUAAAAgRUhITV5gYHRdYl93XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxg + XnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxg + XnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxgXnlcYF55XGBeeVxg + XnlcYF55XGBeeVxgXnlcYF55XGBeeV1hX3hfYWF1TE9PUAgICCEAAAATAAAACAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA0gICAffoOCw4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJ + h/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJ + h/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJ + h/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+EiYf/hImH/4SJh/+BhoXSOzs7JgAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAANd317kIuPjvrU2Nb/8PPy//b39v/29/b/9vf2//b3 + 9v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b3 + 9v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b3 + 9v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//b39v/29/b/9vf2//L0 + 8//a3tz/j5SR+oCFg6gAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4SKiO2+xcH/9ff2/+Xp + 5//d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j + 4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j + 4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j4P/d4+D/3ePg/93j + 4P/d4+D/3ePg/93j4P/j6OX/9Pb1/8zRzv+Fioj7MjIyDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAaEiYf/2t3c/+zv7f/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ + 1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ + 1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ + 1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/+js6v/k5+b/hImH/3d3dyIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhImH/93g3v/q7ez/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa + 1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa + 1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa + 1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//T2tf/09rX/9Pa1//o6+r/5+no/4SJ + h/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//d4N7/6u7s/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/6Ozq/+fp6P+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/3eDf/+vu + 7P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb + 2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb + 2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb + 2P/V29j/1dvY/9Xb2P/V29j/1dvY/+ns6v/n6ej/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAhImH/93g3//r7u3/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc + 2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc + 2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc + 2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/p7Ov/6Orp/4SJh/+Hh4ceAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//e4d//7O/t/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd + 2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd + 2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd + 2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/193a/9fd2v/X3dr/6u3r/+jq + 6f+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/3uHg/+zv7v/X3tv/197b/9fe + 2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe + 2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe + 2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe + 2//X3tv/197b/+ru7P/o6un/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/97h + 4P/s7+7/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je + 3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je + 3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je + 3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/q7uz/6Orq/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAISJh//e4eD/7fDv/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf + 3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf + 3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf + 3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/6+7t/+jr6v+EiYf/h4eHHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/3+Lg/+3w7//a4N3/2uDd/9rg3f/a4N3/2uDd/9rg + 3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg + 3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg + 3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/9rg3f/a4N3/2uDd/+vv + 7f/p6+r/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/9/i4f/u8O//2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/s7+7/6evq/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJ + h//f4uH/7vHw/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh + 3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh + 3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh + 3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//7O/u/+nr6/+EiYf/h4eHHgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACEiYf/4OLh/+/x8P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i + 4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i + 4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i + 4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/+3w7//q7Ov/hImH/4eH + hx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/+Dj4v/v8fH/3uPh/97j4f/e4+H/3uPh/97j + 4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j + 4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j + 4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j4f/e4+H/3uPh/97j + 4f/u8O//6uzr/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//g4+L/8PHx/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/7vDw/+rs7P+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACEiYf/4ePi//Dy8f/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk + 4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk + 4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk + 4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+/x8P/q7Oz/hImH/4eHhx4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhImH/+Hj4v/x8vH/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl + 4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl + 4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl + 4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//v8fD/6+zs/4SJ + h/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//h5OP/8fPy/+Hm5P/h5uT/4ebk/+Hm + 5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm + 5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm + 5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm5P/h5uT/4ebk/+Hm + 5P/h5uT/7/Lx/+vt7P+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/4eTj//Hz + 8v/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl//Dy8f/r7ez/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAhImH/+Lk4//x8/P/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn + 5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn + 5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn + 5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/w8vL/7O3t/4SJh/+Hh4ceAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//i5OP/8vTz/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To + 5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To + 5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To + 5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/8fPy/+zt + 7f+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/4uTj//L08//l6Of/5ejn/+Xo + 5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo + 5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo + 5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo5//l6Of/5ejn/+Xo + 5//l6Of/5ejn//Hz8v/s7e3/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/+Pk + 5P/z9PT/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/y8/P/7O7t/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAISJh//j5OT/8/X0/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq + 6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq + 6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq + 6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/8vTz/+3u7v+EiYf/h4eHHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/4+Tk//T19f/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq + 6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq + 6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq + 6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq//P0 + 9P/t7u7/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/+Tl5P/09fX/6evr/+nr + 6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr + 6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr + 6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr6//p6+v/6evr/+nr + 6//p6+v/6evr/+nr6//z9PT/7e7u/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJ + h//k5eX/9Pb2/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/8/X1/+3u7v+EiYf/h4eHHgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACEiYf/5OXl//X29v/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt + 7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt + 7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt + 7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s//T19f/u7+7/hImH/4eH + hx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhImH/+Tl5f/19vb/6+3t/+vt7f/r7e3/6+3t/+vt + 7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt + 7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt + 7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt + 7f/09fX/7u/v/4SJh/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//k5uX/9vf3/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/9fb2/+7v7/+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACEiYf/5Obl//b39//s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u//X29v/u7+//hImH/4eHhx4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhImH/+Tm5f/29/f/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/19vb/7u/v/4SJ + h/+Hh4ceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//k5uX/9vf3/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/9fb2/+7v7/+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/5Obl//b3 + 9//s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u//X29v/u7+//hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAhImH/+Tm5f/29/f/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/19vb/7u/v/4SJh/+Hh4ceAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISJh//h4+L/9fb2/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/9PX1/+vs + 7P+EiYf/h4eHHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEiYf/3t/f//P19f/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u//P09P/o6en/hImH/4eHhx4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi1s5/9K1 + nf/kzbb/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ez + kv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ez + kv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ez + kv/Xs5L/17OS/9ezkv/Xs5L/17OS/9ezkv/jy7P/28Ou/4xbOP+MbF0xAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIZJH//Ko4P/3LiY/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/27aU/9Owkv+GSR//iGhROAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGSR//yJ99/9u1k//PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/9qz + kP/Rq4z/hkkf/4hoUTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhkkf/8Wde//Ys5D/zJtt/8yb + bf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8yb + bf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8yb + bf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8ybbf/Mm23/zJtt/8yb + bf/Mm23/zJtt/8ybbf/XsY3/z6mK/4ZJH/+IaFE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIZJ + H//DmXj/1a6M/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iU + Z//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iU + Z//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/yJRn/8iU + Z//IlGf/yJRn/8iUZ//IlGf/yJRn/8iUZ//IlGf/1KuI/82lh/+GSR//iGhROAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACGSR//wZd1/9Kqh//Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KP + Yf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KP + Yf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KP + Yf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/8KPYf/Cj2H/wo9h/9Cng//Ko4P/hkkf/4Zn + TTEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh0wi/buQbv/QqIb/voha/76IWv++iFr/voha/76I + Wv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76I + Wv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76I + Wv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76IWv++iFr/voha/76I + Wv/Oo4D/xJt6/4ZKH/+HQyEPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIdPJ86jbkj/07CS/8ea + dP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KS + aP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KS + aP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KSaP/Ckmj/wpJo/8KS + aP/Ckmj/wpJo/8KSaP/FmHH/066Q/6t5U/+HTibnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACGRyE1hkof/ax7Vv/LpIX/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9Gt + kP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9Gt + kP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9Gt + kP/RrZD/0a2Q/9GtkP/RrZD/0a2Q/9GtkP/RrZD/zaaH/7KCXv+HSh/9hUkhTAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIdIIF6GSh/1hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJ + H/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJ + H/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJ + H/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSR//hkkf/4ZJH/+GSSD6hkgecwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAikUWC4dHITyGSiBVhkogVYZK + IFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZK + IFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZK + IFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZKIFWGSiBVhkogVYZK + IFWGSB5Cf0gjDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP////////////////////////////////gAAAAAAAAf8AAAAAAAAA/gAAAAAAAAB+AA + AAAAAAAH4AAAAAAAAAfAAAAAAAAAB+AAAAAAAAAH4AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAA + AAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AA + AAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAA + AAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AA + AAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAA + AAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AA + AAAAAAAH8AAAAAAAAAfwAAAAAAAAB/AAAAAAAAAP8AAAAAAAAA/4AAAAAAAAH/wAAAAAAAA///////// + ////////////////////////////////////////////////////////KAAAADAAAABgAAAAAQAgAAAA + AACAJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA7PDwEFRYWCBYWFgoWFxYKFhcWChcYGAoYGBgKGBkYChgZGAoYGRgKGBkYChgZGAoYGRgKGBkYChgZ + GAoYGRgKGBkYChgZGAoYGRgKGBkYChgZGAoYGRgKGBkYChgZGAoYGRgKGBkYChgZGAoYGRgKGBkYChgZ + GAoYGRgKGBkYChgZGAoYGRgKGBgYChcYFwoWFxcKFhcXChYXFgg9Pj4DAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAD5APwoAAAAUAAAAGwAAAB0AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAA + AB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAA + AB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHQAAABoAAAATQEFBCAAA + AAAAAAAAAAAAAAAAAAAAAAAASkxLBAAAABQREhIpKy0tRy4wL08uMC9RLjAvUS4wL1EuMC9RLjAvUS4w + L1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4w + L1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvUS4wL1EuMC9RLjAvTy0u + LkgVFRUpAQEBE0xOTgMAAAAAAAAAAAAAAAAAAAAAICEgCSkqKjxpbWy2eH172Xp/fd15fnzdeX583Xp+ + fN15fnzden583Xl+fN15fnzdeX583Xp+fN15fnzdeX583Xp+fN15fnzdeX583Xp+fN15fnzdeX583Xp+ + fN15fnzdeX583Xp+fN15fnzdeX583Xp+fN15fnzden583Xl+fN15fnzden583Xl+fN15fnzden583Xl+ + fN15fnzden993Xl9fNpscG+8OTo6QiEiIQcAAAAAAAAAAAAAAAAAAAAAIiMiCYGHhby6vrz96ezr//Dy + 8P/v8vD/7/Lw//Dy8P/v8vD/8PLw/+/y8P/v8vD/7/Lw//Dy8P/v8vD/7/Lw//Dy8P/v8vD/7/Lw//Dy + 8P/v8vD/7/Lw//Dy8P/v8vD/7/Lw//Dy8P/v8vD/7/Lw//Dy8P/v8vD/8PLw/+/y8P/v8vD/8PLw/+/y + 8P/v8vD/8PLw/+/y8P/v8vD/8PLw/+rt7P+/w8H9iI2LzCwtLQkAAAAAAAAAAAAAAAAAAAAAW11cBpab + mfje4uD+4OXj/tfe2//X3dr+193a/tfe2//X3dr+197b/9fd2v7X3dr+193a/tfe2//X3dr+193a/tfe + 2//X3dr+193a/tfe2//X3dr+193a/tfe2//X3dr+193a/tfe2//X3dr+193a/tfe2//X3dr+197b/9fd + 2v7X3dr+197b/9fd2v7X3dr+197b/9fd2v7X3dr+197b/9/k4f7i5uT+mZ6c/WNkZBIAAAAAAAAAAAAA + AAAAAAAAlZmXAZqenP/j5uT+2N7c/tLZ1v/S2db+0tnW/tLZ1v/S2db+0tnW/9LZ1v7S2db+0tnW/tLZ + 1v/S2db+0tnW/tLZ1v/S2db+0tnW/tLZ1v/S2db+0tnW/tLZ1v/S2db+0tnW/tLZ1v/S2db+0tnW/tLZ + 1v/S2db+0tnW/9LZ1v7S2db+0tnW/9LZ1v7S2db+0tnW/9LZ1v7S2db+0tnW/9je2/7n6ej+nKCf/4iI + iBcAAAAAAAAAAAAAAAAAAAAAAAAAAJqfnf/j5+X/2d/c/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9nf + 3P/n6un/nKGf/46PjhYAAAAAAAAAAAAAAAAAAAAAAAAAAJqenf/j5uX+2uDd/tXb2P/V29j+1dvY/tXb + 2P/V29j+1dvY/9Xb2P7V29j+1dvY/tXb2P/V29j+1dvY/tXb2P/V29j+1dvY/tXb2P/V29j+1dvY/tXb + 2P/V29j+1dvY/tXb2P/V29j+1dvY/tXb2P/V29j+1dvY/9Xb2P7V29j+1dvY/9Xb2P7V29j+1dvY/9Xb + 2P7V29j+1dvY/9rf3P7o6un+nKGf/5GSkhYAAAAAAAAAAAAAAAAAAAAAAAAAAJqfnf/k5+b/3OHe/9fd + 2v/W3Nn/1tzZ/9fd2v/W3Nn/193a/9bc2f/W3Nn/1tzZ/9fd2v/W3Nn/1tzZ/9fd2v/W3Nn/1tzZ/9fd + 2v/W3Nn/1tzZ/9fd2v/W3Nn/1tzZ/9fd2v/W3Nn/1tzZ/9fd2v/W3Nn/193a/9bc2f/W3Nn/193a/9bc + 2f/W3Nn/193a/9bc2f/W3Nn/193a/9vg3v/p6+r/naGf/5SVlRYAAAAAAAAAAAAAAAAAAAAAAAAAAJqf + nf/k5+b+3OLf/tfe2//X3dv+193b/tfe2//X3dv+197b/9fd2/7X3dv+193b/tfe2//X3dv+193b/tfe + 2//X3dv+193b/tfe2//X3dv+193b/tfe2//X3dv+193b/tfe2//X3dv+193b/tfe2//X3dv+197b/9fd + 2/7X3dv+197b/9fd2/7X3dv+197b/9fd2/7X3dv+197b/9vh3/7o6+r+naGf/5aXlxYAAAAAAAAAAAAA + AAAAAAAAAAAAAJqfnf/l6Of+3eLg/tje3P/Y3tz+2N7c/tje3P/Y3tz+2N7c/9je3P7Y3tz+2N7c/tje + 3P/Y3tz+2N7c/tje3P/Y3tz+2N7c/tje3P/Y3tz+2N7c/tje3P/Y3tz+2N7c/tje3P/Y3tz+2N7c/tje + 3P/Y3tz+2N7c/9je3P7Y3tz+2N7c/9je3P7Y3tz+2N7c/9je3P7Y3tz+2N7c/9zi4P7p7Ov+naGf/5eY + lxYAAAAAAAAAAAAAAAAAAAAAAAAAAJqfnf/l6Of+3uPh/tnf3f/Z39z+2d/c/tnf3f/Z39z+2d/d/9nf + 3P7Z39z+2d/c/tnf3f/Z39z+2d/c/tnf3f/Z39z+2d/c/tnf3f/Z39z+2d/c/tnf3f/Z39z+2d/c/tnf + 3f/Z39z+2d/c/tnf3f/Z39z+2d/d/9nf3P7Z39z+2d/d/9nf3P7Z39z+2d/d/9nf3P7Z39z+2d/d/97j + 4P7p7Ov+naGf/5eYlxYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnf/m6ej/4OTi/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9/k4v/q7ez/naGg/5eYmBYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnf/m6ej+4OXj/tzh + 3//c4d/+3OHf/tzh3//c4d/+3OHf/9zh3/7c4d/+3OHf/tzh3//c4d/+3OHf/tzh3//c4d/+3OHf/tzh + 3//c4d/+3OHf/tzh3//c4d/+3OHf/tzh3//c4d/+3OHf/tzh3//c4d/+3OHf/9zh3/7c4d/+3OHf/9zh + 3/7c4d/+3OHf/9zh3/7c4d/+3OHf/+Dk4/7q7ez+naGg/5eYmBYAAAAAAAAAAAAAAAAAAAAAAAAAAJuf + nf/n6en+4ubk/t3i4P/d4uD+3eLg/t3i4P/d4uD+3eLg/93i4P7d4uD+3eLg/t3i4P/d4uD+3eLg/t3i + 4P/d4uD+3eLg/t3i4P/d4uD+3eLg/t3i4P/d4uD+3eLg/t3i4P/d4uD+3eLg/t3i4P/d4uD+3eLg/93i + 4P7d4uD+3eLg/93i4P7d4uD+3eLg/93i4P7d4uD+3eLg/+Hm5P7r7ez+naGg/5eYmBYAAAAAAAAAAAAA + AAAAAAAAAAAAAJufnv/o6un/4+bl/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/+Pm5f/s7u7/naGg/5iZ + mBYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnf/o6un+5Ofm/uDk4v/g5OL+4OTi/uDk4v/g5OL+4OTi/+Dk + 4v7g5OL+4OTi/uDk4v/g5OL+4OTi/uDk4v/g5OL+4OTi/uDk4v/g5OL+4OTi/uDk4v/g5OL+4OTi/uDk + 4v/g5OL+4OTi/uDk4v/g5OL+4OTi/+Dk4v7g5OL+4OTi/+Dk4v7g5OL+4OTi/+Dk4v7g5OL+4OTi/+Tn + 5f7s7u3+naGg/5iZmBYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnv/o6+r+5Ojn/uHl4//g5eP+4OXj/uHl + 4//g5eP+4eXj/+Dl4/7g5eP+4OXj/uHl4//g5eP+4OXj/uHl4//g5eP+4OXj/uHl4//g5eP+4OXj/uHl + 4//g5eP+4OXj/uHl4//g5eP+4OXj/uHl4//g5eP+4eXj/+Dl4/7g5eP+4eXj/+Dl4/7g5eP+4eXj/+Dl + 4/7g5eP+4eXj/+To5v7s7+7+naKg/5iZmBYAAAAAAAAAAAAAAAAAAAAAAAAAAJugnv/p6+r/5uno/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Xp6P/t7+7/nqKg/5iZmRYAAAAAAAAAAAAAAAAAAAAAAAAAAJuf + nv/p6+r+5urp/uPn5v/j5+b+4+fm/uPn5v/j5+b+4+fm/+Pn5v7j5+b+4+fm/uPn5v/j5+b+4+fm/uPn + 5v/j5+b+4+fm/uPn5v/j5+b+4+fm/uPn5v/j5+b+4+fm/uPn5v/j5+b+4+fm/uPn5v/j5+b+4+fm/+Pn + 5v7j5+b+4+fm/+Pn5v7j5+b+4+fm/+Pn5v7j5+b+4+fm/+bq6f7u7+/+nqKg/5iZmRYAAAAAAAAAAAAA + AAAAAAAAAAAAAJufnv/p6+r+6Orp/uTo5//k5+b+5Ofm/uTo5//k5+b+5Ojn/+Tn5v7k5+b+5Ofm/uTo + 5//k5+b+5Ofm/uTo5//k5+b+5Ofm/uTo5//k5+b+5Ofm/uTo5//k5+b+5Ofm/uTo5//k5+b+5Ofm/uTo + 5//k5+b+5Ojn/+Tn5v7k5+b+5Ojn/+Tn5v7k5+b+5Ojn/+Tn5v7k5+b+5Ojn/+fq6f7u7+/+nqKg/5iZ + mRYAAAAAAAAAAAAAAAAAAAAAAAAAAJygnv/r7Oz/6ezr/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+nr + 6//v8PD/nqKg/5mZmRYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnv/r7Oz+6uzs/ufq6f/n6en+5+np/ufq + 6f/n6en+5+rp/+fp6f7n6en+5+np/ufq6f/n6en+5+np/ufq6f/n6en+5+np/ufq6f/n6en+5+np/ufq + 6f/n6en+5+np/ufq6f/n6en+5+np/ufq6f/n6en+5+rp/+fp6f7n6en+5+rp/+fp6f7n6en+5+rp/+fp + 6f7n6en+5+rp/+rs6/7v8PD+nqKg/5mZmRYAAAAAAAAAAAAAAAAAAAAAAAAAAJygnv/r7Oz+6+3t/ujq + 6v/o6ur+6Orq/ujq6v/o6ur+6Orq/+jq6v7o6ur+6Orq/ujq6v/o6ur+6Orq/ujq6v/o6ur+6Orq/ujq + 6v/o6ur+6Orq/ujq6v/o6ur+6Orq/ujq6v/o6ur+6Orq/ujq6v/o6ur+6Orq/+jq6v7o6ur+6Orq/+jq + 6v7o6ur+6Orq/+jq6v7o6ur+6Orq/+vt7f7v8PD+nqKg/5mamRYAAAAAAAAAAAAAAAAAAAAAAAAAAJyg + nv/s7e3/7O7u/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+vu7v/w8fH/nqKg/5mamhYAAAAAAAAAAAAA + AAAAAAAAAAAAAJygnv/s7e3+7e/u/urt7P/q7Oz+6uzs/urt7P/q7Oz+6u3s/+rs7P7q7Oz+6uzs/urt + 7P/q7Oz+6uzs/urt7P/q7Oz+6uzs/urt7P/q7Oz+6uzs/urt7P/q7Oz+6uzs/urt7P/q7Oz+6uzs/urt + 7P/q7Oz+6u3s/+rs7P7q7Oz+6u3s/+rs7P7q7Oz+6u3s/+rs7P7q7Oz+6u3s/+zu7v7w8fH+nqKg/5ma + mhYAAAAAAAAAAAAAAAAAAAAAAAAAAJygnv/s7u3+7u/v/uvt7f/r7e3+6+3t/uvt7f/r7e3+6+3t/+vt + 7f7r7e3+6+3t/uvt7f/r7e3+6+3t/uvt7f/r7e3+6+3t/uvt7f/r7e3+6+3t/uvt7f/r7e3+6+3t/uvt + 7f/r7e3+6+3t/uvt7f/r7e3+6+3t/+vt7f7r7e3+6+3t/+vt7f7r7e3+6+3t/+vt7f7r7e3+6+3t/+3v + 7/7x8vL+nqKh/5mamhYAAAAAAAAAAAAAAAAAAAAAAAAAAJygnv/t7u7/7vDw/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+7w8P/x8vL/nqKh/5mZmBYAAAAAAAAAAAAAAAAAAAAAAAAAAJygnv/s7u3+7vDw/uzu + 7v/r7e3+6+3t/uzu7v/r7e3+7O7u/+vt7f7r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu + 7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+7O7u/+vt7f7r7e3+7O7u/+vt + 7f7r7e3+7O7u/+vt7f7r7e3+7O7u/+7v7/7x8vL+nqKh/5mYlhYAAAAAAAAAAAAAAAAAAAAAAAAAAJyg + nv/t7u7/7vDw/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+7w8P/x8vL/nqKh/5iWlBYAAAAAAAAAAAAA + AAAAAAAAAAAAAJugnv/s7e3+7vDw/uzu7v/r7e3+6+3t/uzu7v/r7e3+7O7u/+vt7f7r7e3+6+3t/uzu + 7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu + 7v/r7e3+7O7u/+vt7f7r7e3+7O7u/+vt7f7r7e3+7O7u/+vt7f7r7e3+7O7u/+7v7/7w8fH+nqKg/5iU + kRYAAAAAAAAAAAAAAAAAAAAAAAAAAJufnf/p6+r+7e/v/uzu7v/r7e3+6+3t/uzu7v/r7e3+7O7u/+vt + 7f7r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu7v/r7e3+6+3t/uzu + 7v/r7e3+6+3t/uzu7v/r7e3+7O7u/+vt7f7r7e3+7O7u/+vt7f7r7e3+7O7u/+vt7f7r7e3+7O7u/+3v + 7/7u7+/+naGf/5eTjxYAAAAAAAAAAAAAAAAAAAAAAAAAAJx8ZP/ey7n/38ew/9zBqf/cwan/3MGp/9zB + qf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zB + qf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zBqf/cwan/3MGp/9zB + qf/cwan/3MGp/9/Gr//i0MD/n4Bo/5qCdSEAAAAAAAAAAAAAAAAAAAAAAAAAAJdfN//SrIz+0qR6/s+e + cf/OnXD+zp1w/s+ecf/OnXD+z55x/86dcP7OnXD+zp1w/s+ecf/OnXD+zp1w/s+ecf/OnXD+zp1w/s+e + cf/OnXD+zp1w/s+ecf/OnXD+zp1w/s+ecf/OnXD+zp1w/s+ecf/OnXD+z55x/86dcP7OnXD+z55x/86d + cP7OnXD+z55x/86dcP7OnXD+z55x/9Gjef7WsZH+mWI7/5d4YikAAAAAAAAAAAAAAAAAAAAAAAAAAJZe + Nv/PqIb+0KJ3/s2cb//NnG7+zZxu/s2cb//NnG7+zZxv/82cbv7NnG7+zZxu/s2cb//NnG7+zZxu/s2c + b//NnG7+zZxu/s2cb//NnG7+zZxu/s2cb//NnG7+zZxu/s2cb//NnG7+zZxu/s2cb//NnG7+zZxv/82c + bv7NnG7+zZxv/82cbv7NnG7+zZxv/82cbv7NnG7+zZxv/9Chdv7UrYz+mGE6/5V2XykAAAAAAAAAAAAA + AAAAAAAAAAAAAJVdNf/MpIL/zJxx/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mV + aP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mV + aP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8mVaP/JlWj/yZVo/8ybcP/RqYj/mGA5/5V1 + XCkAAAAAAAAAAAAAAAAAAAAAAAAAAJRcNP7In3z+xZRp/sGNX//AjV/+wI1f/sGNX//AjV/+wY1f/8CN + X/7AjV/+wI1f/sGNX//AjV/+wI1f/sGNX//AjV/+wI1f/sGNX//AjV/+wI1f/sGNX//AjV/+wI1f/sGN + X//AjV/+wI1f/sGNX//AjV/+wY1f/8CNX/7AjV/+wY1f/8CNX/7AjV/+wY1f/8CNX/7AjV/+wY1f/8ST + aP7Lo4H+ll83/5NrTx4AAAAAAAAAAAAAAAAAAAAAAAAAAJFZMuzAlXP+xpdw/sCNYf+/jGD+v4xg/sCN + Yf+/jGD+wI1h/7+MYP6/jGD+v4xg/sCNYf+/jGD+v4xg/sCNYf+/jGD+v4xg/sCNYf+/jGD+v4xg/sCN + Yf+/jGD+v4xg/sCNYf+/jGD+v4xg/sCNYf+/jGD+wI1h/7+MYP6/jGD+wI1h/7+MYP6/jGD+wI1h/7+M + YP6/jGD+wI1h/8WWbv7DmXf+klsz9aVxTgUAAAAAAAAAAAAAAAAAAAAAAAAAAIhLJIOhbUf+xZt5/82m + hv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82m + hv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82mhv/Npob/zaaG/82m + hv/Npob/zaaG/82mhv/Npob/zaaG/8ade/+lcUv+iE0klQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx3 + ThGMTyWAhkgeyYZJH9SFSR/UhUkf1IZJH9SFSR/Uhkkf1IVJH9SFSR/UhUkf1IZJH9SFSR/UhUkf1IZJ + H9SFSR/UhUkf1IZJH9SFSR/UhUkf1IZJH9SFSR/UhUkf1IZJH9SFSR/UhUkf1IZJH9SFSR/Uhkkf1IVJ + H9SFSR/Uhkkf1IVJH9SFSR/Uhkkf1IVJH9SFSR/Uhkkf1IVIH8uKTyaKqnZOFQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACocEUCnGI6F5xkOyqcZDsqnGU7Kp1lOyqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5m + PCqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5m + PCqeZjwqnmY8Kp5mPCqeZjwqnmY8Kp5mPCqeZjwqnmY8KpxkOyqcZDsqm2Q7KppjOhqkcEkDAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /////wAA////////AADwAAAAAA8AAOAAAAAABwAAwAAAAAADAADAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAA + AAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAAD + AADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAA + AAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAOAAAAAAAwAA4AAAAAAD + AADgAAAAAAMAAOAAAAAAAwAA4AAAAAADAADgAAAAAAcAAOAAAAAABwAA8AAAAAAPAAD///////8AAP// + /////wAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACstLAUsLS0HLS4uBzAxMAcxMjEHMTIxBzEyMQcxMjEHMTIxBzEy + MQcxMjEHMTIxBzEyMQcxMjEHMTIxBzEyMQcxMjEHMTIxBzEyMQcxMjEHMTIxBzEyMQcwMTEHLjAvBy0u + LgctLi0FAAAAAAAAAAAAAAAAAAAAAAAAAAAbHBwOAAAAHQAAACEAAAAiAAAAIgAAACIAAAAiAAAAIgAA + ACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAA + ACIAAAAiAAAAIQAAABwcHR0NAAAAAAAAAAAAAAAAP0FABScoKEVqbm2vcHVyu3B0crxwdHK8cHRyvHB0 + crxwdHK8cHRyvHB0crxwdHK8cHRyvHB0crxwdHK8cHRyvHB0crxwdHK8cHRyvHB0crxwdHK8cHRyvHB0 + crxwdHK8cHRyvHB0crxwdHK7bHBvsDEyMktCQ0IEAAAAAAAAAABFR0YGkZaU3efq6f/p7ev/6e3r/+nt + 6//p7ev/6e3r/+nt6//p7ev/6e3r/+nt6//p7ev/6e3r/+nt6//p7ev/6e3r/+nt6//p7ev/6e3r/+nt + 6//p7ev/6e3r/+nt6//p7ev/6e3r/+nt6//o7Or/mJ2a51NVVAYAAAAAAAAAAJSYlgGvs7L/3uPh/9LZ + 1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ + 1v/S2db/0tnW/9LZ1v/S2db/0tnW/9LZ1v/S2db/0tnW/93i4P+0uLf/ioyLEAAAAAAAAAAAAAAAALC0 + sv/f5OH/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb + 1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/1NvX/9Tb1//U29f/3uPg/7W5t/+WmJcPAAAAAAAA + AAAAAAAAsLSz/+Hl4//W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc + 2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/W3Nn/1tzZ/9bc2f/g5OL/trm4/6Cj + og8AAAAAAAAAAAAAAACxtbP/4ebk/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe + 2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/9fe2//X3tv/197b/+Dm + 4/+2ubj/pqinDwAAAAAAAAAAAAAAALG1s//j5+b/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf + 3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf3f/Z393/2d/d/9nf + 3f/Z393/4ufl/7a6uP+nqagPAAAAAAAAAAAAAAAAsbW0/+To5//b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg3v/b4N7/2+De/9vg + 3v/b4N7/2+De/9vg3v/j5+b/trq4/6iqqQ8AAAAAAAAAAAAAAACytbT/5uno/93i4P/d4uD/3eLg/93i + 4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i4P/d4uD/3eLg/93i + 4P/d4uD/3eLg/93i4P/d4uD/3eLg/+Xp5/+3urn/qKqpDwAAAAAAAAAAAAAAALK2tP/n6un/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j + 4v/f4+L/3+Pi/9/j4v/f4+L/3+Pi/9/j4v/f4+L/5+rp/7e6uf+pq6oPAAAAAAAAAAAAAAAAsra0/+ns + 6v/h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl + 4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//h5eP/4eXj/+Hl4//o6+r/t7q5/6mrqg8AAAAAAAAAAAAA + AACytrX/6ezs/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm + 5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+Lm5f/i5uX/4ubl/+ns6/+3u7n/qqurDwAA + AAAAAAAAAAAAALO2tf/r7u3/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To + 5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/5Ojn/+To5//k6Of/6u3s/7i7 + uv+qrKsPAAAAAAAAAAAAAAAAs7a1/+zv7v/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp6P/m6ej/5uno/+bp + 6P/s7u3/uLu6/6usrA8AAAAAAAAAAAAAAACztrX/7u/v/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq + 6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq6v/o6ur/6Orq/+jq + 6v/o6ur/6Orq/+3v7/+4u7r/q62sDwAAAAAAAAAAAAAAALS3tv/v8fH/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns7P/p7Oz/6ezs/+ns + 7P/p7Oz/6ezs/+ns7P/p7Oz/7vDw/7i7uv+sra0PAAAAAAAAAAAAAAAAtLe2//Dy8v/r7e3/6+3t/+vt + 7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt + 7f/r7e3/6+3t/+vt7f/r7e3/6+3t/+vt7f/w8fH/uby7/6ytrQ8AAAAAAAAAAAAAAAC0t7b/8fLy/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u//Dy8v+5vLv/q6upDwAAAAAAAAAAAAAAALS3 + tv/x8vL/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/8PLy/7m8u/+qp6IPAAAAAAAA + AAAAAAAAs7a1//Dy8v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu7v/w8fH/uLu6/6mi + mw8AAAAAAAAAAAAAAACvno//5tjK/+HQwP/h0MD/4dDA/+HQwP/h0MD/4dDA/+HQwP/h0MD/4dDA/+HQ + wP/h0MD/4dDA/+HQwP/h0MD/4dDA/+HQwP/h0MD/4dDA/+HQwP/h0MD/4dDA/+HQwP/h0MD/4dDA/+bY + yf+0pJX/qJaJEwAAAAAAAAAAAAAAAKd1T//VqoP/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+ecf/PnnH/z55x/8+e + cf/PnnH/1KmB/6x7V/+liHIcAAAAAAAAAAAAAAAApXJM/9CkfP/Kl2r/ypdq/8qXav/Kl2r/ypdq/8qX + av/Kl2r/ypdq/8qXav/Kl2r/ypdq/8qXav/Kl2r/ypdq/8qXav/Kl2r/ypdq/8qXav/Kl2r/ypdq/8qX + av/Kl2r/ypdq/8qXav/Ponr/qnhT/6KCaRwAAAAAAAAAAAAAAACib0n+yJpy/8CLXf/Ai13/wItd/8CL + Xf/Ai13/wItd/8CLXf/Ai13/wItd/8CLXf/Ai13/wItd/8CLXf/Ai13/wItd/8CLXf/Ai13/wItd/8CL + Xf/Ai13/wItd/8CLXf/Ai13/wItd/8eYb/+mdE7/n3RWEAAAAAAAAAAAAAAAAI1TK7/Emnj/yZ98/8mf + fP/Jn3z/yZ98/8mffP/Jn3z/yZ98/8mffP/Jn3z/yZ98/8mffP/Jn3z/yZ98/8mffP/Jn3z/yZ98/8mf + fP/Jn3z/yZ98/8mffP/Jn3z/yZ98/8mffP/Jn3z/xZt5/49WLssAAAAAAAAAAAAAAAAAAAAAqHNKF4dH + HY6GSR+qhkkfqoZJH6qGSR+qhkkfqoZJH6qGSR+qhkkfqoZJH6qGSR+qhkkfqoZJH6qGSR+qhkkfqoZJ + H6qGSR+qhkkfqoZJH6qGSR+qhkkfqoZJH6qGSR+qhkkfqoZJH6qESCCSp3JKHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA/////+AAAAfAAAADgAAAAYAAAAGAAAABwAAAAcAAAAHAAAABwAAAAcAA + AAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAA + AAHAAAABwAAAA8AAAAP///////////////8oAAAAGAAAADAAAAABACAAAAAAAGAJAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpdXAIUFBQOCwsLFAsL + CxQMDAwUDAwMFAwMDBQMDAwUDAwMFAwMDBQMDAwUDAwMFAwMDBQMDAwUDAwMFAwMDBQMDAwUDAwMFAsM + DBQLCwsUFRUVDl5gXwIAAAAAAAAAACUmJRdHSkmAVFdWllRXVZdTV1WXVFdVl1RXVZdTV1WXVFdVl1RX + VZdUV1WXU1dVl1RXVZdTV1WXVFdVl1RXVZdTV1WXVFdVl1RXVZdUV1WWSkxLgiorKhgAAAAAAAAAAGVo + Z3HY3Nr+4+jl/+Po5f/j5+X+4+jl/+Po5f/j5+X+4+jl/+Po5f/j6OX/4+fl/uPo5f/j5+X+4+jl/+Po + 5f/j5+X+4+jl/+Po5f/j5+X+29/d/mxvbnkAAAAAAAAAAJufnX/e4uD+09rX/9Pa1//T2tb+09rX/9Pa + 1//T2tb+09rX/9Pa1//T2tf/09rW/tPa1//T2tb+09rX/9Pa1//T2tb+09rX/9Pa1//T2tb+4OTi/5OW + lYoAAAAAAAAAAKmurH/f4+H+1tzZ/tbc2f7W3Nn+1tzZ/tbc2f7W3Nn+1tzZ/tbc2f7W3Nn+1tzZ/tbc + 2f7W3Nn+1tzZ/tbc2f7W3Nn+1tzZ/tbc2f7W3Nn+4eXj/peamYoAAAAAAAAAALO4tn/h5eP+197b/9fe + 2//X3tv+197b/9fe2//X3tv+197b/9fe2//X3tv/197b/tfe2//X3tv+197b/9fe2//X3tv+197b/9fe + 2//X3tv+4ufl/5mcm4oAAAAAAAAAALW6uH/i5uT+2uDd/9rg3f/a393+2uDd/9rg3f/a393+2uDd/9rg + 3f/a4N3/2t/d/trg3f/a393+2uDd/9rg3f/a393+2uDd/9rg3f/a393+5Ojm/5qcm4oAAAAAAAAAALa6 + uX/k5+b+3eLg/t3i4P7d4uD+3eLg/t3i4P7d4uD+3eLg/t3i4P7d4uD+3eLg/t3i4P7d4uD+3eLg/t3i + 4P7d4uD+3eLg/t3i4P7d4uD+5uno/pqdnIoAAAAAAAAAALe7uX/m6Of+3+Pi/9/j4v/f4+L+3+Pi/9/j + 4v/f4+L+3+Pi/9/j4v/f4+L/3+Pi/t/j4v/f4+L+3+Pi/9/j4v/f4+L+3+Pi/9/j4v/f4+L+6Orp/5qd + nIoAAAAAAAAAALi8un/n6un+4ebk/+Hm5P/h5eT+4ebk/+Hm5P/h5eT+4ebk/+Hm5P/h5uT/4eXk/uHm + 5P/h5eT+4ebk/+Hm5P/h5eT+4ebk/+Hm5P/h5eT+6ezr/5udnIoAAAAAAAAAALm9u3/o6+r+5Ofm/+Tn + 5v/k5+b+5Ofm/+Tn5v/k5+b+5Ofm/+Tn5v/k5+b/5Ofm/uTn5v/k5+b+5Ofm/+Tn5v/k5+b+5Ofm/+Tn + 5v/k5+b+6u3s/5udnIoAAAAAAAAAALq9vH/q7Ov+5uno/ubp6P7m6ej+5uno/ubp6P7m6ej+5uno/ubp + 6P7m6ej+5uno/ubp6P7m6ej+5uno/ubp6P7m6ej+5uno/ubp6P7m6ej+7O7t/puenYoAAAAAAAAAALq+ + vX/r7e3+6evr/+nr6//o6+v+6evr/+nr6//o6+v+6evr/+nr6//p6+v/6Ovr/unr6//o6+v+6evr/+nr + 6//o6+v+6evr/+nr6//o6+v+7e/v/5uenYoAAAAAAAAAALu+vX/t7u7+6+3t/uvt7f7r7e3+6+3t/uvt + 7f7r7e3+6+3t/uvt7f7r7e3+6+3t/uvt7f7r7e3+6+3t/uvt7f7r7e3+6+3t/uvt7f7r7e3+7/Dw/pye + nYoAAAAAAAAAALq7uH/t7+/+7O7u/+zu7v/r7e3+7O7u/+zu7v/r7e3+7O7u/+zu7v/s7u7/6+3t/uzu + 7v/r7e3+7O7u/+zu7v/r7e3+7O7u/+zu7v/r7e3+7/Hx/5udnIoAAAAAAAAAALi1rn/t7+7+7O7u/+zu + 7v/r7e3+7O7u/+zu7v/r7e3+7O7u/+zu7v/s7u7/6+3t/uzu7v/r7e3+7O7u/+zu7v/r7e3+7O7u/+zu + 7v/r7e3+7/Hx/5ucmooAAAAAAAAAALWllX/l29H+5NfL/uTXy/7k18v+5NfL/uTXy/7k18v+5NfL/uTX + y/7k18v+5NfL/uTXy/7k18v+5NfL/uTXy/7k18v+5NfL/uTXy/7k18v+593T/puNg40AAAAAAAAAAK6E + ZH/Rp4H+zp1w/86dcP/OnW/+zp1w/86dcP/OnW/+zp1w/86dcP/OnXD/zp1v/s6dcP/OnW/+zp1w/86d + cP/OnW/+zp1w/86dcP/OnW/+06mD/5dsTZQAAAAAAAAAAKp8WX/JnXb+xZFj/8WRY//EkWP+xZFj/8WR + Y//EkWP+xZFj/8WRY//FkWP/xJFj/sWRY//EkWP+xZFj/8WRY//EkWP+xZFj/8WRY//EkWP+y554/5Vo + R5EAAAAAAAAAAKJvSFu7jWn+xplz/saZc/7GmXP+xplz/saZc/7GmXP+xplz/saZc/7GmXP+xplz/saZ + c/7GmXP+xplz/saZc/7GmXP+xplz/saZc/7GmXP+vY9r/p1oQWQAAAAAAAAAALB9VASWWjFZkVYtf5FX + LX+RVy1/klctf5JXLX+RVy1/klctf5JXLX+SVy1/kVctf5JXLX+RVy1/klctf5JXLX+RVy1/klctf5FX + LX+QVi1/k1oyXa98VAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAA + AQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAP// + /wD///8AKAAAABAAAAAgAAAAAQAgAAAAAABABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8Pj0BMjMzAzY3 + NgM3ODcDNzg3Azc4NwM3ODcDNzg3Azc4NwM3ODcDNzg3AzY4NwMzNDQDPkA/AQAAAABlZ2YBKywsSDg6 + OW44OjlvODo5bzg6OW84OjlvODo5bzg6OW84OjlvODo5bzg6OW84OjlvODo5bi4wL0lqbGsBe359AcHG + xPbe4+D/3uPg/97j4P/e4+D/3uPg/97j4P/e4+D/3uPg/97j4P/e4+D/3uPg/97j4P/Eycf5f4GABQAA + AADIzMr/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/1dvY/9Xb2P/V29j/ys7M/6Om + pQcAAAAAyc7M/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/9je3P/Y3tz/2N7c/8vQ + zv+0t7YHAAAAAMvPzf/c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh3//c4d//3OHf/9zh + 3//N0c//trm4BwAAAADN0M//4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk4v/g5OL/4OTi/+Dk + 4v/g5OL/z9LR/7i6uQcAAAAAztHQ/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn5v/j5+b/4+fm/+Pn + 5v/j5+b/4+fm/9DT0v+5u7sHAAAAANDT0v/n6un/5+rp/+fq6f/n6un/5+rp/+fq6f/n6un/5+rp/+fq + 6f/n6un/5+rp/+fq6f/S1dT/u7y8BwAAAADR1NP/6u3s/+rt7P/q7ez/6u3s/+rt7P/q7ez/6u3s/+rt + 7P/q7ez/6u3s/+rt7P/q7ez/1NbV/7y9vQcAAAAA0tXU/+zu7v/s7u7/7O7u/+zu7v/s7u7/7O7u/+zu + 7v/s7u7/7O7u/+zu7v/s7u7/7O7u/9TX1v+6t7MHAAAAAM7HwP/m39f/5t/X/+bf1//m39f/5t/X/+bf + 1//m39f/5t/X/+bf1//m39f/5t/X/+bf1//QysL/tqicCAAAAAC8jWb/zJpt/8yabf/Mmm3/zJpt/8ya + bf/Mmm3/zJpt/8yabf/Mmm3/zJpt/8yabf/Mmm3/vo9p/7CSeg4AAAAAr31X78SVbP/ElWz/xJVs/8SV + bP/ElWz/xJVs/8SVbP/ElWz/xJVs/8SVbP/ElWz/xJVs/7B/WfKvhGMEAAAAAKRsQymbYzpVnGQ7VZ1l + O1WdZTtVnWU7VZ1lO1WdZTtVnWU7VZ1lO1WdZTtVnWU7VZtjOlWibEQrAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAAAAAAAAA + AACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIABAAD//wAA + + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditNestInfoForm.Designer.cs b/Source/OpenNest/Forms/EditNestInfoForm.Designer.cs new file mode 100644 index 0000000..77e60b6 --- /dev/null +++ b/Source/OpenNest/Forms/EditNestInfoForm.Designer.cs @@ -0,0 +1,709 @@ +namespace OpenNest.Forms +{ + partial class EditNestInfoForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.nameBox = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.labelEdgeSpacingLeft = new System.Windows.Forms.Label(); + this.labelEdgeSpacingTop = new System.Windows.Forms.Label(); + this.labelEdgeSpacingRight = new System.Windows.Forms.Label(); + this.labelEdgeSpacingBottom = new System.Windows.Forms.Label(); + this.leftSpacingBox = new OpenNest.Controls.NumericUpDown(); + this.topSpacingBox = new OpenNest.Controls.NumericUpDown(); + this.rightSpacingBox = new OpenNest.Controls.NumericUpDown(); + this.bottomSpacingBox = new OpenNest.Controls.NumericUpDown(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.labelSize = new System.Windows.Forms.Label(); + this.sizeBox = new System.Windows.Forms.TextBox(); + this.labelPartSpacing = new System.Windows.Forms.Label(); + this.partSpacingBox = new OpenNest.Controls.NumericUpDown(); + this.labelThk = new System.Windows.Forms.Label(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.quadrantSelect1 = new OpenNest.Controls.QuadrantSelect(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.radioButton1 = new System.Windows.Forms.RadioButton(); + this.radioButton2 = new System.Windows.Forms.RadioButton(); + this.label2 = new System.Windows.Forms.Label(); + this.thicknessBox = new OpenNest.Controls.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.customerBox = new System.Windows.Forms.TextBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.notesBox = new System.Windows.Forms.TextBox(); + this.cancelButton = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.applyButton = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.leftSpacingBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.topSpacingBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.rightSpacingBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.bottomSpacingBox)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.partSpacingBox)).BeginInit(); + this.groupBox3.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.tableLayoutPanel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.thicknessBox)).BeginInit(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 11); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(126, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Name :"; + // + // nameBox + // + this.nameBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.nameBox.Location = new System.Drawing.Point(135, 8); + this.nameBox.Name = "nameBox"; + this.nameBox.ReadOnly = true; + this.nameBox.Size = new System.Drawing.Size(224, 22); + this.nameBox.TabIndex = 1; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Location = new System.Drawing.Point(6, 100); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(279, 176); + this.groupBox1.TabIndex = 3; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Edge Spacing"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingLeft, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingTop, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingRight, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingBottom, 0, 3); + this.tableLayoutPanel2.Controls.Add(this.leftSpacingBox, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.topSpacingBox, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.rightSpacingBox, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.bottomSpacingBox, 1, 3); + this.tableLayoutPanel2.Location = new System.Drawing.Point(6, 19); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 4; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(267, 151); + this.tableLayoutPanel2.TabIndex = 0; + // + // labelEdgeSpacingLeft + // + this.labelEdgeSpacingLeft.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingLeft.AutoSize = true; + this.labelEdgeSpacingLeft.Location = new System.Drawing.Point(3, 10); + this.labelEdgeSpacingLeft.Name = "labelEdgeSpacingLeft"; + this.labelEdgeSpacingLeft.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingLeft.TabIndex = 0; + this.labelEdgeSpacingLeft.Text = "Left :"; + this.labelEdgeSpacingLeft.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingTop + // + this.labelEdgeSpacingTop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingTop.AutoSize = true; + this.labelEdgeSpacingTop.Location = new System.Drawing.Point(3, 47); + this.labelEdgeSpacingTop.Name = "labelEdgeSpacingTop"; + this.labelEdgeSpacingTop.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingTop.TabIndex = 2; + this.labelEdgeSpacingTop.Text = "Top :"; + this.labelEdgeSpacingTop.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingRight + // + this.labelEdgeSpacingRight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingRight.AutoSize = true; + this.labelEdgeSpacingRight.Location = new System.Drawing.Point(3, 84); + this.labelEdgeSpacingRight.Name = "labelEdgeSpacingRight"; + this.labelEdgeSpacingRight.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingRight.TabIndex = 4; + this.labelEdgeSpacingRight.Text = "Right :"; + this.labelEdgeSpacingRight.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingBottom + // + this.labelEdgeSpacingBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingBottom.AutoSize = true; + this.labelEdgeSpacingBottom.Location = new System.Drawing.Point(3, 123); + this.labelEdgeSpacingBottom.Name = "labelEdgeSpacingBottom"; + this.labelEdgeSpacingBottom.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingBottom.TabIndex = 6; + this.labelEdgeSpacingBottom.Text = "Bottom :"; + this.labelEdgeSpacingBottom.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // leftSpacingBox + // + this.leftSpacingBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.leftSpacingBox.DecimalPlaces = 4; + this.leftSpacingBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.leftSpacingBox.Location = new System.Drawing.Point(65, 7); + this.leftSpacingBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.leftSpacingBox.Name = "leftSpacingBox"; + this.leftSpacingBox.Size = new System.Drawing.Size(199, 22); + this.leftSpacingBox.Suffix = ""; + this.leftSpacingBox.TabIndex = 1; + // + // topSpacingBox + // + this.topSpacingBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.topSpacingBox.DecimalPlaces = 4; + this.topSpacingBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.topSpacingBox.Location = new System.Drawing.Point(65, 44); + this.topSpacingBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.topSpacingBox.Name = "topSpacingBox"; + this.topSpacingBox.Size = new System.Drawing.Size(199, 22); + this.topSpacingBox.Suffix = ""; + this.topSpacingBox.TabIndex = 3; + // + // rightSpacingBox + // + this.rightSpacingBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.rightSpacingBox.DecimalPlaces = 4; + this.rightSpacingBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.rightSpacingBox.Location = new System.Drawing.Point(65, 81); + this.rightSpacingBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.rightSpacingBox.Name = "rightSpacingBox"; + this.rightSpacingBox.Size = new System.Drawing.Size(199, 22); + this.rightSpacingBox.Suffix = ""; + this.rightSpacingBox.TabIndex = 5; + // + // bottomSpacingBox + // + this.bottomSpacingBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.bottomSpacingBox.DecimalPlaces = 4; + this.bottomSpacingBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.bottomSpacingBox.Location = new System.Drawing.Point(65, 120); + this.bottomSpacingBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.bottomSpacingBox.Name = "bottomSpacingBox"; + this.bottomSpacingBox.Size = new System.Drawing.Size(199, 22); + this.bottomSpacingBox.Suffix = ""; + this.bottomSpacingBox.TabIndex = 7; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.labelSize, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.sizeBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.labelPartSpacing, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.partSpacingBox, 1, 1); + this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 6); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(267, 78); + this.tableLayoutPanel1.TabIndex = 2; + // + // labelSize + // + this.labelSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelSize.AutoSize = true; + this.labelSize.Location = new System.Drawing.Point(3, 11); + this.labelSize.Name = "labelSize"; + this.labelSize.Size = new System.Drawing.Size(91, 16); + this.labelSize.TabIndex = 0; + this.labelSize.Text = "Size :"; + this.labelSize.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // sizeBox + // + this.sizeBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.sizeBox.Location = new System.Drawing.Point(100, 8); + this.sizeBox.Name = "sizeBox"; + this.sizeBox.Size = new System.Drawing.Size(164, 22); + this.sizeBox.TabIndex = 1; + this.sizeBox.TextChanged += new System.EventHandler(this.sizeBox_TextChanged); + // + // labelPartSpacing + // + this.labelPartSpacing.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelPartSpacing.AutoSize = true; + this.labelPartSpacing.Location = new System.Drawing.Point(3, 50); + this.labelPartSpacing.Name = "labelPartSpacing"; + this.labelPartSpacing.Size = new System.Drawing.Size(91, 16); + this.labelPartSpacing.TabIndex = 2; + this.labelPartSpacing.Text = "Part Spacing :"; + this.labelPartSpacing.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // partSpacingBox + // + this.partSpacingBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.partSpacingBox.DecimalPlaces = 4; + this.partSpacingBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.partSpacingBox.Location = new System.Drawing.Point(100, 47); + this.partSpacingBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.partSpacingBox.Name = "partSpacingBox"; + this.partSpacingBox.Size = new System.Drawing.Size(164, 22); + this.partSpacingBox.Suffix = ""; + this.partSpacingBox.TabIndex = 1; + // + // labelThk + // + this.labelThk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelThk.AutoSize = true; + this.labelThk.Location = new System.Drawing.Point(3, 128); + this.labelThk.Name = "labelThk"; + this.labelThk.Size = new System.Drawing.Size(126, 16); + this.labelThk.TabIndex = 6; + this.labelThk.Text = "Thickness :"; + // + // groupBox3 + // + this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox3.Controls.Add(this.quadrantSelect1); + this.groupBox3.Location = new System.Drawing.Point(306, 6); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(315, 270); + this.groupBox3.TabIndex = 7; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Quadrant"; + // + // quadrantSelect1 + // + this.quadrantSelect1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.quadrantSelect1.BackColor = System.Drawing.Color.White; + this.quadrantSelect1.Location = new System.Drawing.Point(6, 19); + this.quadrantSelect1.Name = "quadrantSelect1"; + this.quadrantSelect1.Quadrant = 1; + this.quadrantSelect1.Size = new System.Drawing.Size(303, 245); + this.quadrantSelect1.TabIndex = 5; + this.quadrantSelect1.Text = "quadrantSelect1"; + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.ItemSize = new System.Drawing.Size(100, 22); + this.tabControl1.Location = new System.Drawing.Point(12, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(635, 453); + this.tabControl1.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabControl1.TabIndex = 0; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.tableLayoutPanel3); + this.tabPage1.Location = new System.Drawing.Point(4, 26); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(627, 423); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Info"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Controls.Add(this.tableLayoutPanel4, 1, 5); + this.tableLayoutPanel3.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel3.Controls.Add(this.nameBox, 1, 0); + this.tableLayoutPanel3.Controls.Add(this.label2, 0, 4); + this.tableLayoutPanel3.Controls.Add(this.labelThk, 0, 3); + this.tableLayoutPanel3.Controls.Add(this.thicknessBox, 1, 3); + this.tableLayoutPanel3.Controls.Add(this.label3, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.label4, 0, 2); + this.tableLayoutPanel3.Controls.Add(this.customerBox, 1, 4); + this.tableLayoutPanel3.Controls.Add(this.textBox1, 1, 1); + this.tableLayoutPanel3.Controls.Add(this.textBox2, 1, 2); + this.tableLayoutPanel3.Controls.Add(this.label5, 0, 5); + this.tableLayoutPanel3.Location = new System.Drawing.Point(6, 6); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 6; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(362, 240); + this.tableLayoutPanel3.TabIndex = 0; + // + // tableLayoutPanel4 + // + this.tableLayoutPanel4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel4.ColumnCount = 2; + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel4.Controls.Add(this.radioButton1, 0, 0); + this.tableLayoutPanel4.Controls.Add(this.radioButton2, 1, 0); + this.tableLayoutPanel4.Location = new System.Drawing.Point(135, 198); + this.tableLayoutPanel4.Name = "tableLayoutPanel4"; + this.tableLayoutPanel4.RowCount = 1; + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel4.Size = new System.Drawing.Size(224, 39); + this.tableLayoutPanel4.TabIndex = 1; + // + // radioButton1 + // + this.radioButton1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.radioButton1.AutoSize = true; + this.radioButton1.Location = new System.Drawing.Point(3, 9); + this.radioButton1.Name = "radioButton1"; + this.radioButton1.Size = new System.Drawing.Size(106, 20); + this.radioButton1.TabIndex = 0; + this.radioButton1.TabStop = true; + this.radioButton1.Text = "Inches"; + this.radioButton1.UseVisualStyleBackColor = true; + this.radioButton1.CheckedChanged += new System.EventHandler(this.Units_Changed); + // + // radioButton2 + // + this.radioButton2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.radioButton2.AutoSize = true; + this.radioButton2.Location = new System.Drawing.Point(115, 9); + this.radioButton2.Name = "radioButton2"; + this.radioButton2.Size = new System.Drawing.Size(106, 20); + this.radioButton2.TabIndex = 0; + this.radioButton2.TabStop = true; + this.radioButton2.Text = "Millimeters"; + this.radioButton2.UseVisualStyleBackColor = true; + this.radioButton2.CheckedChanged += new System.EventHandler(this.Units_Changed); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 167); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(126, 16); + this.label2.TabIndex = 8; + this.label2.Text = "Customer :"; + // + // thicknessBox + // + this.thicknessBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.thicknessBox.DecimalPlaces = 4; + this.thicknessBox.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.thicknessBox.Location = new System.Drawing.Point(135, 125); + this.thicknessBox.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.thicknessBox.Name = "thicknessBox"; + this.thicknessBox.Size = new System.Drawing.Size(224, 22); + this.thicknessBox.Suffix = ""; + this.thicknessBox.TabIndex = 7; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 50); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(126, 16); + this.label3.TabIndex = 2; + this.label3.Text = "Date Created :"; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(3, 89); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(126, 16); + this.label4.TabIndex = 4; + this.label4.Text = "Date Last Modified :"; + // + // customerBox + // + this.customerBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.customerBox.Location = new System.Drawing.Point(135, 164); + this.customerBox.Name = "customerBox"; + this.customerBox.Size = new System.Drawing.Size(224, 22); + this.customerBox.TabIndex = 9; + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(135, 47); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(224, 22); + this.textBox1.TabIndex = 3; + // + // textBox2 + // + this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.textBox2.Location = new System.Drawing.Point(135, 86); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + this.textBox2.Size = new System.Drawing.Size(224, 22); + this.textBox2.TabIndex = 5; + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(3, 209); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(126, 16); + this.label5.TabIndex = 8; + this.label5.Text = "Units :"; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.groupBox3); + this.tabPage2.Controls.Add(this.groupBox1); + this.tabPage2.Controls.Add(this.tableLayoutPanel1); + this.tabPage2.Location = new System.Drawing.Point(4, 26); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(627, 423); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Defaults"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.notesBox); + this.tabPage3.Location = new System.Drawing.Point(4, 26); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(627, 423); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Notes"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // notesBox + // + this.notesBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.notesBox.Location = new System.Drawing.Point(6, 6); + this.notesBox.Multiline = true; + this.notesBox.Name = "notesBox"; + this.notesBox.Size = new System.Drawing.Size(615, 408); + this.notesBox.TabIndex = 2; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(557, 10); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.applyButton); + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 471); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(659, 50); + this.bottomPanel1.TabIndex = 1; + // + // applyButton + // + this.applyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.applyButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.applyButton.Enabled = false; + this.applyButton.Location = new System.Drawing.Point(461, 10); + this.applyButton.Name = "applyButton"; + this.applyButton.Size = new System.Drawing.Size(90, 28); + this.applyButton.TabIndex = 0; + this.applyButton.Text = "Done"; + this.applyButton.UseVisualStyleBackColor = true; + // + // EditNestInfoForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(659, 521); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditNestInfoForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Nest"; + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.leftSpacingBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.topSpacingBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.rightSpacingBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.bottomSpacingBox)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.partSpacingBox)).EndInit(); + this.groupBox3.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel3.PerformLayout(); + this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.thicknessBox)).EndInit(); + this.tabPage2.ResumeLayout(false); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox nameBox; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label labelEdgeSpacingLeft; + private System.Windows.Forms.Label labelEdgeSpacingTop; + private System.Windows.Forms.Label labelEdgeSpacingRight; + private System.Windows.Forms.Label labelEdgeSpacingBottom; + private Controls.NumericUpDown leftSpacingBox; + private Controls.NumericUpDown topSpacingBox; + private Controls.NumericUpDown rightSpacingBox; + private Controls.NumericUpDown bottomSpacingBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label labelSize; + private System.Windows.Forms.TextBox sizeBox; + private Controls.NumericUpDown thicknessBox; + private System.Windows.Forms.Label labelThk; + private System.Windows.Forms.Label labelPartSpacing; + private Controls.NumericUpDown partSpacingBox; + private Controls.BottomPanel bottomPanel1; + private System.Windows.Forms.GroupBox groupBox3; + private Controls.QuadrantSelect quadrantSelect1; + private System.Windows.Forms.Button applyButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.TextBox notesBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox customerBox; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; + private System.Windows.Forms.RadioButton radioButton1; + private System.Windows.Forms.RadioButton radioButton2; + private System.Windows.Forms.Label label5; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditNestInfoForm.cs b/Source/OpenNest/Forms/EditNestInfoForm.cs new file mode 100644 index 0000000..76b26f4 --- /dev/null +++ b/Source/OpenNest/Forms/EditNestInfoForm.cs @@ -0,0 +1,234 @@ +using System; +using System.Windows.Forms; +using Timer = System.Timers.Timer; + +namespace OpenNest.Forms +{ + public partial class EditNestInfoForm : Form + { + private readonly Timer timer; + private DateTime dateCreated; + private DateTime dateLastModified; + + public EditNestInfoForm() + { + InitializeComponent(); + + timer = new Timer + { + SynchronizingObject = this, + Enabled = true, + AutoReset = false, + Interval = SystemInformation.KeyboardDelay + 1 + }; + timer.Elapsed += (sender, e) => EnableCheck(); + EnableCheck(); + } + + public string NestName + { + get { return nameBox.Text; } + private set { nameBox.Text = value; } + } + + public string Customer + { + get { return customerBox.Text; } + set { customerBox.Text = value; } + } + + public string Notes + { + get { return notesBox.Text; } + set { notesBox.Text = value; } + } + + public string SizeString + { + get { return sizeBox.Text; } + set { sizeBox.Text = value; } + } + + public DateTime DateCreated + { + get { return dateCreated; } + set + { + dateCreated = value; + textBox1.Text = dateCreated.ToShortDateString(); + } + } + + public DateTime DateLastModified + { + get { return dateLastModified; } + set + { + dateLastModified = value; + textBox2.Text = dateLastModified.ToShortDateString(); + } + } + + public double LeftSpacing + { + get { return (double)leftSpacingBox.Value; } + set { leftSpacingBox.Value = (decimal)value; } + } + + public double TopSpacing + { + get { return (double)topSpacingBox.Value; } + set { topSpacingBox.Value = (decimal)value; } + } + + public double RightSpacing + { + get { return (double)rightSpacingBox.Value; } + set { rightSpacingBox.Value = (decimal)value; } + } + + public double BottomSpacing + { + get { return (double)bottomSpacingBox.Value; } + set { bottomSpacingBox.Value = (decimal)value; } + } + + public double PartSpacing + { + get { return (double)partSpacingBox.Value; } + set { partSpacingBox.Value = (decimal)value; } + } + + public double Thickness + { + get { return (double)thicknessBox.Value; } + set { thicknessBox.Value = (decimal)value; } + } + + public void SetUnits(Units units) + { + switch (units) + { + case OpenNest.Units.Inches: + radioButton1.Checked = true; + break; + + case OpenNest.Units.Millimeters: + radioButton2.Checked = true; + break; + } + + UpdateUnitSuffix(); + } + + public Units GetUnits() + { + if (radioButton1.Checked) + return Units.Inches; + + if (radioButton2.Checked) + return Units.Millimeters; + + return OpenNest.Units.Inches; + } + + private void UpdateUnitSuffix() + { + var unitBoxes = new Controls.NumericUpDown[] + { + thicknessBox, + partSpacingBox, + leftSpacingBox, + topSpacingBox, + rightSpacingBox, + bottomSpacingBox + }; + + var unitString = " " + UnitsHelper.GetShortString(GetUnits()); + + foreach (var box in unitBoxes) + box.Suffix = unitString; + } + + public int Quadrant + { + get { return quadrantSelect1.Quadrant; } + set { quadrantSelect1.Quadrant = value; } + } + + public void EnableCheck() + { + Size size; + + if (!OpenNest.Size.TryParse(sizeBox.Text, out size)) + { + applyButton.Enabled = false; + return; + } + + if (LeftSpacing + RightSpacing >= size.Width) + { + applyButton.Enabled = false; + return; + } + + if (TopSpacing + BottomSpacing >= size.Height) + { + applyButton.Enabled = false; + return; + } + + applyButton.Enabled = true; + } + + public void LoadNestInfo(Nest nest) + { + NestName = nest.Name; + Notes = nest.Notes; + Customer = nest.Customer; + DateCreated = nest.DateCreated; + DateLastModified = nest.DateLastModified; + Thickness = nest.PlateDefaults.Thickness; + SizeString = nest.PlateDefaults.Size.ToString(); + PartSpacing = nest.PlateDefaults.PartSpacing; + LeftSpacing = nest.PlateDefaults.EdgeSpacing.Left; + TopSpacing = nest.PlateDefaults.EdgeSpacing.Top; + RightSpacing = nest.PlateDefaults.EdgeSpacing.Right; + BottomSpacing = nest.PlateDefaults.EdgeSpacing.Bottom; + Quadrant = nest.PlateDefaults.Quadrant; + + SetUnits(nest.Units); + } + + public void SaveNestInfo(Nest nest) + { + nest.Name = NestName; + nest.Units = GetUnits(); + nest.Notes = Notes; + nest.Customer = Customer; + nest.DateCreated = DateCreated; + nest.DateLastModified = DateLastModified; + nest.PlateDefaults.Thickness = Thickness; + nest.PlateDefaults.Size = OpenNest.Size.Parse(SizeString); + nest.PlateDefaults.PartSpacing = PartSpacing; + nest.PlateDefaults.EdgeSpacing = new Spacing(LeftSpacing, BottomSpacing, RightSpacing, TopSpacing); + nest.PlateDefaults.Quadrant = Quadrant; + } + + private void sizeBox_TextChanged(object sender, System.EventArgs e) + { + timer.Stop(); + timer.Start(); + } + + private void Units_Changed(object sender, EventArgs e) + { + var radioButton = sender as RadioButton; + + if (radioButton == null || !radioButton.Checked) + return; + + UpdateUnitSuffix(); + } + } +} diff --git a/Source/OpenNest/Forms/EditNestInfoForm.resx b/Source/OpenNest/Forms/EditNestInfoForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/EditNestInfoForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditPlateForm.Designer.cs b/Source/OpenNest/Forms/EditPlateForm.Designer.cs new file mode 100644 index 0000000..415b10a --- /dev/null +++ b/Source/OpenNest/Forms/EditPlateForm.Designer.cs @@ -0,0 +1,450 @@ +namespace OpenNest.Forms +{ + partial class EditPlateForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.labelQty = new System.Windows.Forms.Label(); + this.labelSize = new System.Windows.Forms.Label(); + this.textBoxSize = new System.Windows.Forms.TextBox(); + this.labelThk = new System.Windows.Forms.Label(); + this.labelPartSpacing = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.labelEdgeSpacingLeft = new System.Windows.Forms.Label(); + this.labelEdgeSpacingTop = new System.Windows.Forms.Label(); + this.labelEdgeSpacingRight = new System.Windows.Forms.Label(); + this.labelEdgeSpacingBottom = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.applyButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.quadrantSelect1 = new OpenNest.Controls.QuadrantSelect(); + this.numericUpDownEdgeSpacingLeft = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownEdgeSpacingTop = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownEdgeSpacingRight = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownEdgeSpacingBottom = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownQty = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownThickness = new OpenNest.Controls.NumericUpDown(); + this.numericUpDownPartSpacing = new OpenNest.Controls.NumericUpDown(); + this.tableLayoutPanel1.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.bottomPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingLeft)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingTop)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingRight)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingBottom)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownQty)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownThickness)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPartSpacing)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.labelQty, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.labelSize, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.textBoxSize, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.numericUpDownQty, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.numericUpDownThickness, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.labelThk, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.labelPartSpacing, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.numericUpDownPartSpacing, 1, 3); + this.tableLayoutPanel1.Location = new System.Drawing.Point(18, 12); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(236, 144); + this.tableLayoutPanel1.TabIndex = 0; + // + // labelQty + // + this.labelQty.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelQty.AutoSize = true; + this.labelQty.Location = new System.Drawing.Point(3, 46); + this.labelQty.Name = "labelQty"; + this.labelQty.Size = new System.Drawing.Size(91, 16); + this.labelQty.TabIndex = 2; + this.labelQty.Text = "Quantity :"; + this.labelQty.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelSize + // + this.labelSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelSize.AutoSize = true; + this.labelSize.Location = new System.Drawing.Point(3, 10); + this.labelSize.Name = "labelSize"; + this.labelSize.Size = new System.Drawing.Size(91, 16); + this.labelSize.TabIndex = 0; + this.labelSize.Text = "Size :"; + this.labelSize.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // textBoxSize + // + this.textBoxSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxSize.Location = new System.Drawing.Point(100, 7); + this.textBoxSize.Name = "textBoxSize"; + this.textBoxSize.Size = new System.Drawing.Size(133, 22); + this.textBoxSize.TabIndex = 1; + this.textBoxSize.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // labelThk + // + this.labelThk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelThk.AutoSize = true; + this.labelThk.Location = new System.Drawing.Point(3, 82); + this.labelThk.Name = "labelThk"; + this.labelThk.Size = new System.Drawing.Size(91, 16); + this.labelThk.TabIndex = 4; + this.labelThk.Text = "Thickness :"; + this.labelThk.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelPartSpacing + // + this.labelPartSpacing.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelPartSpacing.AutoSize = true; + this.labelPartSpacing.Location = new System.Drawing.Point(3, 118); + this.labelPartSpacing.Name = "labelPartSpacing"; + this.labelPartSpacing.Size = new System.Drawing.Size(91, 16); + this.labelPartSpacing.TabIndex = 6; + this.labelPartSpacing.Text = "Part Spacing :"; + this.labelPartSpacing.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Location = new System.Drawing.Point(12, 173); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(248, 175); + this.groupBox1.TabIndex = 1; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Edge Spacing"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingLeft, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingTop, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingRight, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.labelEdgeSpacingBottom, 0, 3); + this.tableLayoutPanel2.Controls.Add(this.numericUpDownEdgeSpacingLeft, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.numericUpDownEdgeSpacingTop, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.numericUpDownEdgeSpacingRight, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.numericUpDownEdgeSpacingBottom, 1, 3); + this.tableLayoutPanel2.Location = new System.Drawing.Point(6, 21); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 4; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(236, 148); + this.tableLayoutPanel2.TabIndex = 0; + // + // labelEdgeSpacingLeft + // + this.labelEdgeSpacingLeft.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingLeft.AutoSize = true; + this.labelEdgeSpacingLeft.Location = new System.Drawing.Point(3, 10); + this.labelEdgeSpacingLeft.Name = "labelEdgeSpacingLeft"; + this.labelEdgeSpacingLeft.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingLeft.TabIndex = 0; + this.labelEdgeSpacingLeft.Text = "Left :"; + this.labelEdgeSpacingLeft.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingTop + // + this.labelEdgeSpacingTop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingTop.AutoSize = true; + this.labelEdgeSpacingTop.Location = new System.Drawing.Point(3, 47); + this.labelEdgeSpacingTop.Name = "labelEdgeSpacingTop"; + this.labelEdgeSpacingTop.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingTop.TabIndex = 2; + this.labelEdgeSpacingTop.Text = "Top :"; + this.labelEdgeSpacingTop.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingRight + // + this.labelEdgeSpacingRight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingRight.AutoSize = true; + this.labelEdgeSpacingRight.Location = new System.Drawing.Point(3, 84); + this.labelEdgeSpacingRight.Name = "labelEdgeSpacingRight"; + this.labelEdgeSpacingRight.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingRight.TabIndex = 4; + this.labelEdgeSpacingRight.Text = "Right :"; + this.labelEdgeSpacingRight.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // labelEdgeSpacingBottom + // + this.labelEdgeSpacingBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelEdgeSpacingBottom.AutoSize = true; + this.labelEdgeSpacingBottom.Location = new System.Drawing.Point(3, 121); + this.labelEdgeSpacingBottom.Name = "labelEdgeSpacingBottom"; + this.labelEdgeSpacingBottom.Size = new System.Drawing.Size(56, 16); + this.labelEdgeSpacingBottom.TabIndex = 6; + this.labelEdgeSpacingBottom.Text = "Bottom :"; + this.labelEdgeSpacingBottom.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // groupBox2 + // + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox2.Controls.Add(this.quadrantSelect1); + this.groupBox2.Location = new System.Drawing.Point(277, 12); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(261, 209); + this.groupBox2.TabIndex = 6; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Quadrant"; + // + // applyButton + // + this.applyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.applyButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.applyButton.Enabled = false; + this.applyButton.Location = new System.Drawing.Point(352, 11); + this.applyButton.Name = "applyButton"; + this.applyButton.Size = new System.Drawing.Size(90, 28); + this.applyButton.TabIndex = 3; + this.applyButton.Text = "Done"; + this.applyButton.UseVisualStyleBackColor = true; + this.applyButton.Click += new System.EventHandler(this.button1_Click); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(448, 11); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.applyButton); + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 359); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(550, 50); + this.bottomPanel1.TabIndex = 7; + // + // quadrantSelect1 + // + this.quadrantSelect1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.quadrantSelect1.Location = new System.Drawing.Point(6, 19); + this.quadrantSelect1.Name = "quadrantSelect1"; + this.quadrantSelect1.Quadrant = 1; + this.quadrantSelect1.Size = new System.Drawing.Size(249, 184); + this.quadrantSelect1.TabIndex = 5; + this.quadrantSelect1.Text = "quadrantSelect1"; + // + // numericUpDownEdgeSpacingLeft + // + this.numericUpDownEdgeSpacingLeft.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownEdgeSpacingLeft.DecimalPlaces = 4; + this.numericUpDownEdgeSpacingLeft.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownEdgeSpacingLeft.Location = new System.Drawing.Point(65, 7); + this.numericUpDownEdgeSpacingLeft.Name = "numericUpDownEdgeSpacingLeft"; + this.numericUpDownEdgeSpacingLeft.Size = new System.Drawing.Size(168, 22); + this.numericUpDownEdgeSpacingLeft.Suffix = ""; + this.numericUpDownEdgeSpacingLeft.TabIndex = 1; + this.numericUpDownEdgeSpacingLeft.ValueChanged += new System.EventHandler(this.spacing_ValueChanged); + // + // numericUpDownEdgeSpacingTop + // + this.numericUpDownEdgeSpacingTop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownEdgeSpacingTop.DecimalPlaces = 4; + this.numericUpDownEdgeSpacingTop.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownEdgeSpacingTop.Location = new System.Drawing.Point(65, 44); + this.numericUpDownEdgeSpacingTop.Name = "numericUpDownEdgeSpacingTop"; + this.numericUpDownEdgeSpacingTop.Size = new System.Drawing.Size(168, 22); + this.numericUpDownEdgeSpacingTop.Suffix = ""; + this.numericUpDownEdgeSpacingTop.TabIndex = 3; + this.numericUpDownEdgeSpacingTop.ValueChanged += new System.EventHandler(this.spacing_ValueChanged); + // + // numericUpDownEdgeSpacingRight + // + this.numericUpDownEdgeSpacingRight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownEdgeSpacingRight.DecimalPlaces = 4; + this.numericUpDownEdgeSpacingRight.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownEdgeSpacingRight.Location = new System.Drawing.Point(65, 81); + this.numericUpDownEdgeSpacingRight.Name = "numericUpDownEdgeSpacingRight"; + this.numericUpDownEdgeSpacingRight.Size = new System.Drawing.Size(168, 22); + this.numericUpDownEdgeSpacingRight.Suffix = ""; + this.numericUpDownEdgeSpacingRight.TabIndex = 5; + this.numericUpDownEdgeSpacingRight.ValueChanged += new System.EventHandler(this.spacing_ValueChanged); + // + // numericUpDownEdgeSpacingBottom + // + this.numericUpDownEdgeSpacingBottom.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownEdgeSpacingBottom.DecimalPlaces = 4; + this.numericUpDownEdgeSpacingBottom.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownEdgeSpacingBottom.Location = new System.Drawing.Point(65, 118); + this.numericUpDownEdgeSpacingBottom.Name = "numericUpDownEdgeSpacingBottom"; + this.numericUpDownEdgeSpacingBottom.Size = new System.Drawing.Size(168, 22); + this.numericUpDownEdgeSpacingBottom.Suffix = ""; + this.numericUpDownEdgeSpacingBottom.TabIndex = 7; + this.numericUpDownEdgeSpacingBottom.ValueChanged += new System.EventHandler(this.spacing_ValueChanged); + // + // numericUpDownQty + // + this.numericUpDownQty.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownQty.Location = new System.Drawing.Point(100, 43); + this.numericUpDownQty.Name = "numericUpDownQty"; + this.numericUpDownQty.Size = new System.Drawing.Size(133, 22); + this.numericUpDownQty.Suffix = ""; + this.numericUpDownQty.TabIndex = 3; + // + // numericUpDownThickness + // + this.numericUpDownThickness.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownThickness.DecimalPlaces = 4; + this.numericUpDownThickness.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownThickness.Location = new System.Drawing.Point(100, 79); + this.numericUpDownThickness.Name = "numericUpDownThickness"; + this.numericUpDownThickness.Size = new System.Drawing.Size(133, 22); + this.numericUpDownThickness.Suffix = ""; + this.numericUpDownThickness.TabIndex = 5; + // + // numericUpDownPartSpacing + // + this.numericUpDownPartSpacing.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownPartSpacing.DecimalPlaces = 4; + this.numericUpDownPartSpacing.Increment = new decimal(new int[] { + 25, + 0, + 0, + 131072}); + this.numericUpDownPartSpacing.Location = new System.Drawing.Point(100, 115); + this.numericUpDownPartSpacing.Name = "numericUpDownPartSpacing"; + this.numericUpDownPartSpacing.Size = new System.Drawing.Size(133, 22); + this.numericUpDownPartSpacing.Suffix = ""; + this.numericUpDownPartSpacing.TabIndex = 7; + // + // EditPlateForm + // + this.AcceptButton = this.applyButton; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(550, 409); + this.Controls.Add(this.bottomPanel1); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.tableLayoutPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditPlateForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Plate"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.bottomPanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingLeft)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingTop)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingRight)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownEdgeSpacingBottom)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownQty)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownThickness)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPartSpacing)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label labelSize; + private System.Windows.Forms.Button applyButton; + private System.Windows.Forms.TextBox textBoxSize; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label labelEdgeSpacingLeft; + private System.Windows.Forms.Label labelEdgeSpacingTop; + private System.Windows.Forms.Label labelEdgeSpacingRight; + private System.Windows.Forms.Label labelEdgeSpacingBottom; + private Controls.NumericUpDown numericUpDownEdgeSpacingLeft; + private Controls.NumericUpDown numericUpDownEdgeSpacingTop; + private Controls.NumericUpDown numericUpDownEdgeSpacingRight; + private Controls.NumericUpDown numericUpDownEdgeSpacingBottom; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Label labelQty; + private Controls.NumericUpDown numericUpDownQty; + private Controls.QuadrantSelect quadrantSelect1; + private System.Windows.Forms.GroupBox groupBox2; + private Controls.NumericUpDown numericUpDownThickness; + private System.Windows.Forms.Label labelThk; + private System.Windows.Forms.Label labelPartSpacing; + private Controls.NumericUpDown numericUpDownPartSpacing; + private Controls.BottomPanel bottomPanel1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/EditPlateForm.cs b/Source/OpenNest/Forms/EditPlateForm.cs new file mode 100644 index 0000000..112afad --- /dev/null +++ b/Source/OpenNest/Forms/EditPlateForm.cs @@ -0,0 +1,198 @@ +using System; +using System.Windows.Forms; +using Timer = System.Timers.Timer; + +namespace OpenNest.Forms +{ + public partial class EditPlateForm : Form + { + private readonly Plate plate; + private readonly Timer timer; + + private Units units; + + public EditPlateForm(Plate plate) + { + InitializeComponent(); + + this.plate = plate; + + timer = new Timer + { + SynchronizingObject = this, + Enabled = true, + AutoReset = false, + Interval = SystemInformation.KeyboardDelay + 1 + }; + timer.Elapsed += (sender, e) => EnableCheck(); + + Load(plate); + EnableCheck(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + SetUnits(units); + } + + public Units Units + { + get { return units; } + set + { + units = value; + SetUnits(value); + } + } + + private void SetUnits(Units units) + { + string suffix = " " + UnitsHelper.GetShortString(units); + decimal increment; + + if (units == Units.Inches) + increment = 0.25m; + else + increment = 1; + + var controls = new [] + { + numericUpDownThickness, + numericUpDownPartSpacing, + numericUpDownEdgeSpacingBottom, + numericUpDownEdgeSpacingLeft, + numericUpDownEdgeSpacingRight, + numericUpDownEdgeSpacingTop + }; + + foreach (var control in controls) + { + control.Suffix = suffix; + control.Increment = increment; + control.Invalidate(true); + } + } + + public string SizeString + { + get { return textBoxSize.Text; } + set { textBoxSize.Text = value; } + } + + public double LeftSpacing + { + get { return (double)numericUpDownEdgeSpacingLeft.Value; } + set { numericUpDownEdgeSpacingLeft.Value = (decimal)value; } + } + + public double TopSpacing + { + get { return (double)numericUpDownEdgeSpacingTop.Value; } + set { numericUpDownEdgeSpacingTop.Value = (decimal)value; } + } + + public double RightSpacing + { + get { return (double)numericUpDownEdgeSpacingRight.Value; } + set { numericUpDownEdgeSpacingRight.Value = (decimal)value; } + } + + public double BottomSpacing + { + get { return (double)numericUpDownEdgeSpacingBottom.Value; } + set { numericUpDownEdgeSpacingBottom.Value = (decimal)value; } + } + + public double PartSpacing + { + get { return (double)numericUpDownPartSpacing.Value; } + set { numericUpDownPartSpacing.Value = (decimal)value; } + } + + public double Thickness + { + get { return (double)numericUpDownThickness.Value; } + set { numericUpDownThickness.Value = (decimal)value; } + } + + public int Quantity + { + get { return (int)numericUpDownQty.Value; } + set { numericUpDownQty.Value = value; } + } + + public int Quadrant + { + get { return quadrantSelect1.Quadrant; } + set { quadrantSelect1.Quadrant = value; } + } + + public void EnableCheck() + { + OpenNest.Size size; + + if (!OpenNest.Size.TryParse(SizeString, out size)) + { + applyButton.Enabled = false; + return; + } + + if (LeftSpacing + RightSpacing >= size.Width) + { + applyButton.Enabled = false; + return; + } + + if (TopSpacing + BottomSpacing >= size.Height) + { + applyButton.Enabled = false; + return; + } + + applyButton.Enabled = true; + } + + private new void Load(Plate plate) + { + textBoxSize.Text = plate.Size.ToString(); + LeftSpacing = plate.EdgeSpacing.Left; + TopSpacing = plate.EdgeSpacing.Top; + RightSpacing = plate.EdgeSpacing.Right; + BottomSpacing = plate.EdgeSpacing.Bottom; + PartSpacing = plate.PartSpacing; + Quantity = plate.Quantity; + Quadrant = plate.Quadrant; + Thickness = plate.Thickness; + } + + private void Save() + { + plate.Size = OpenNest.Size.Parse(SizeString); + plate.EdgeSpacing.Left = LeftSpacing; + plate.EdgeSpacing.Top = TopSpacing; + plate.EdgeSpacing.Right = RightSpacing; + plate.EdgeSpacing.Bottom = BottomSpacing; + plate.PartSpacing = PartSpacing; + plate.Quantity = Quantity; + plate.Quadrant = Quadrant; + plate.Thickness = Thickness; + } + + private void textBox1_TextChanged(object sender, EventArgs e) + { + timer.Stop(); + timer.Start(); + } + + private void button1_Click(object sender, EventArgs e) + { + Save(); + } + + private void spacing_ValueChanged(object sender, EventArgs e) + { + EnableCheck(); + } + } +} diff --git a/Source/OpenNest/Forms/EditPlateForm.resx b/Source/OpenNest/Forms/EditPlateForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/EditPlateForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/FillPlateForm.Designer.cs b/Source/OpenNest/Forms/FillPlateForm.Designer.cs new file mode 100644 index 0000000..b228ee3 --- /dev/null +++ b/Source/OpenNest/Forms/FillPlateForm.Designer.cs @@ -0,0 +1,64 @@ +namespace OpenNest.Forms +{ + partial class FillPlateForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.AutoScroll = true; + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(377, 525); + this.tableLayoutPanel1.TabIndex = 2; + // + // FillPlateForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(377, 525); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "FillPlateForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "FillPlateForm"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/FillPlateForm.cs b/Source/OpenNest/Forms/FillPlateForm.cs new file mode 100644 index 0000000..4663d33 --- /dev/null +++ b/Source/OpenNest/Forms/FillPlateForm.cs @@ -0,0 +1,55 @@ +using System.Drawing; +using System.Windows.Forms; +using OpenNest.Collections; +using OpenNest.Controls; + +namespace OpenNest.Forms +{ + public partial class FillPlateForm : Form + { + private DrawingCollection Drawings; + + public FillPlateForm(DrawingCollection drawings) + { + InitializeComponent(); + Drawings = drawings; + UpdateDrawingList(); + } + + public Drawing SelectedDrawing { get; protected set; } + + public void UpdateDrawingList() + { + tableLayoutPanel1.Controls.Clear(); + tableLayoutPanel1.RowStyles.Clear(); + tableLayoutPanel1.RowCount = Drawings.Count + 1; + + var controls = new PlateView[Drawings.Count]; + + int index = 0; + + foreach (var dwg in Drawings) + { + var control = new PlateView(); + control.DrawOrigin = false; + control.AllowPan = false; + control.AllowSelect = false; + control.AllowZoom = false; + control.BackColor = Color.White; + control.Plate.Size = new OpenNest.Size(0, 0); + control.AddPartFromDrawing(dwg, Vector.Zero); + control.MouseDoubleClick += (sender, e) => + { + SelectedDrawing = control.Plate.Parts.Count > 0 ? control.Plate.Parts[0].BaseDrawing : null; + Close(); + }; + control.Dock = DockStyle.Fill; + controls[index] = control; + tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 200)); + index++; + } + + tableLayoutPanel1.Controls.AddRange(controls); + } + } +} diff --git a/Source/OpenNest/Forms/FillPlateForm.resx b/Source/OpenNest/Forms/FillPlateForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/FillPlateForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/MainForm.Designer.cs b/Source/OpenNest/Forms/MainForm.Designer.cs new file mode 100644 index 0000000..1cd9a23 --- /dev/null +++ b/Source/OpenNest/Forms/MainForm.Designer.cs @@ -0,0 +1,1282 @@ +namespace OpenNest.Forms +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFileNew = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFileOpen = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFileSave = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFileSaveAs = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFileExport = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFileExportAll = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFileExit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEdit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEditCut = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEditCopy = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEditPaste = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuEditSelectAll = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuView = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewDrawRapids = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewDrawBounds = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuViewZoomTo = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomToArea = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomToFit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomToPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomToSelected = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomIn = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuViewZoomOut = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTools = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsMeasureArea = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlign = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlignLeft = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlignRight = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlignTop = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlignBottom = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuToolsAlignHorizontal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsAlignVertically = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuToolsEvenlySpaceHorizontal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuToolsEvenlySpaceVertical = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSetOffsetIncrement = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuSetRotationIncrement = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuToolsOptions = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNest = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestEdit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestImportDrawing = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuNestFirstPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestLastPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuNestNextPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestPreviousPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); + this.runAutoNestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.autoSequenceAllPlatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestRemoveEmptyPlates = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNestPost = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem19 = new System.Windows.Forms.ToolStripSeparator(); + this.calculateCutTimeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlate = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateEdit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateSetAsDefault = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem18 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPlateAdd = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateRemove = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPlateFill = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateRotate = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateRotateCw = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlateRotateCcw = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPlateRotate180 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuResizeToFitParts = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPlateViewInCad = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSequenceParts = new System.Windows.Forms.ToolStripMenuItem(); + this.autoSequenceCurrentPlateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.manualSequenceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.calculateCutTimeToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.centerPartsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWindowCascade = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWindowTileVertical = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWindowTileHorizontal = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); + this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuCloseAll = new System.Windows.Forms.ToolStripMenuItem(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.statusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.locationStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.spacerLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.plateIndexStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.plateSizeStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.plateQtyStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.btnNew = new System.Windows.Forms.ToolStripButton(); + this.btnOpen = new System.Windows.Forms.ToolStripButton(); + this.btnSave = new System.Windows.Forms.ToolStripButton(); + this.btnSaveAs = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.btnFirstPlate = new System.Windows.Forms.ToolStripButton(); + this.btnPreviousPlate = new System.Windows.Forms.ToolStripButton(); + this.btnNextPlate = new System.Windows.Forms.ToolStripButton(); + this.btnLastPlate = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.btnZoomOut = new System.Windows.Forms.ToolStripButton(); + this.btnZoomIn = new System.Windows.Forms.ToolStripButton(); + this.btnZoomToFit = new System.Windows.Forms.ToolStripButton(); + this.pEPToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openNestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.menuStrip1.SuspendLayout(); + this.statusStrip1.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuFile, + this.mnuEdit, + this.mnuView, + this.mnuTools, + this.mnuNest, + this.mnuPlate, + this.mnuWindow}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(1098, 24); + this.menuStrip1.TabIndex = 7; + this.menuStrip1.Text = "menuStrip1"; + // + // mnuFile + // + this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuFileNew, + this.mnuFileOpen, + this.toolStripMenuItem1, + this.mnuFileSave, + this.mnuFileSaveAs, + this.toolStripMenuItem2, + this.mnuFileExport, + this.mnuFileExportAll, + this.toolStripMenuItem3, + this.mnuFileExit}); + this.mnuFile.Name = "mnuFile"; + this.mnuFile.Size = new System.Drawing.Size(37, 20); + this.mnuFile.Text = "&File"; + // + // mnuFileNew + // + this.mnuFileNew.Image = global::OpenNest.Properties.Resources.doc_new; + this.mnuFileNew.Name = "mnuFileNew"; + this.mnuFileNew.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); + this.mnuFileNew.Size = new System.Drawing.Size(146, 22); + this.mnuFileNew.Text = "New"; + this.mnuFileNew.Click += new System.EventHandler(this.New_Click); + // + // mnuFileOpen + // + this.mnuFileOpen.Image = global::OpenNest.Properties.Resources.doc_open; + this.mnuFileOpen.Name = "mnuFileOpen"; + this.mnuFileOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); + this.mnuFileOpen.Size = new System.Drawing.Size(146, 22); + this.mnuFileOpen.Text = "Open"; + this.mnuFileOpen.Click += new System.EventHandler(this.Open_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(143, 6); + // + // mnuFileSave + // + this.mnuFileSave.Enabled = false; + this.mnuFileSave.Image = global::OpenNest.Properties.Resources.save; + this.mnuFileSave.Name = "mnuFileSave"; + this.mnuFileSave.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); + this.mnuFileSave.Size = new System.Drawing.Size(146, 22); + this.mnuFileSave.Text = "Save"; + this.mnuFileSave.Click += new System.EventHandler(this.Save_Click); + // + // mnuFileSaveAs + // + this.mnuFileSaveAs.Enabled = false; + this.mnuFileSaveAs.Image = global::OpenNest.Properties.Resources.save_as; + this.mnuFileSaveAs.Name = "mnuFileSaveAs"; + this.mnuFileSaveAs.Size = new System.Drawing.Size(146, 22); + this.mnuFileSaveAs.Text = "Save As"; + this.mnuFileSaveAs.Click += new System.EventHandler(this.SaveAs_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(143, 6); + // + // mnuFileExport + // + this.mnuFileExport.Name = "mnuFileExport"; + this.mnuFileExport.Size = new System.Drawing.Size(146, 22); + this.mnuFileExport.Text = "Export"; + this.mnuFileExport.Click += new System.EventHandler(this.Export_Click); + // + // mnuFileExportAll + // + this.mnuFileExportAll.Name = "mnuFileExportAll"; + this.mnuFileExportAll.Size = new System.Drawing.Size(146, 22); + this.mnuFileExportAll.Text = "Export All"; + this.mnuFileExportAll.Click += new System.EventHandler(this.ExportAll_Click); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(143, 6); + // + // mnuFileExit + // + this.mnuFileExit.Name = "mnuFileExit"; + this.mnuFileExit.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Q))); + this.mnuFileExit.Size = new System.Drawing.Size(146, 22); + this.mnuFileExit.Text = "Exit"; + this.mnuFileExit.Click += new System.EventHandler(this.Exit_Click); + // + // mnuEdit + // + this.mnuEdit.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuEditCut, + this.mnuEditCopy, + this.mnuEditPaste, + this.toolStripMenuItem4, + this.mnuEditSelectAll}); + this.mnuEdit.Name = "mnuEdit"; + this.mnuEdit.Size = new System.Drawing.Size(39, 20); + this.mnuEdit.Text = "&Edit"; + // + // mnuEditCut + // + this.mnuEditCut.Name = "mnuEditCut"; + this.mnuEditCut.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X))); + this.mnuEditCut.Size = new System.Drawing.Size(164, 22); + this.mnuEditCut.Text = "Cut"; + this.mnuEditCut.Click += new System.EventHandler(this.EditCut_Click); + // + // mnuEditCopy + // + this.mnuEditCopy.Name = "mnuEditCopy"; + this.mnuEditCopy.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C))); + this.mnuEditCopy.Size = new System.Drawing.Size(164, 22); + this.mnuEditCopy.Text = "Copy"; + this.mnuEditCopy.Click += new System.EventHandler(this.EditCopy_Click); + // + // mnuEditPaste + // + this.mnuEditPaste.Enabled = false; + this.mnuEditPaste.Name = "mnuEditPaste"; + this.mnuEditPaste.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V))); + this.mnuEditPaste.Size = new System.Drawing.Size(164, 22); + this.mnuEditPaste.Text = "Paste"; + this.mnuEditPaste.Visible = false; + this.mnuEditPaste.Click += new System.EventHandler(this.EditPaste_Click); + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(161, 6); + // + // mnuEditSelectAll + // + this.mnuEditSelectAll.Name = "mnuEditSelectAll"; + this.mnuEditSelectAll.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A))); + this.mnuEditSelectAll.Size = new System.Drawing.Size(164, 22); + this.mnuEditSelectAll.Text = "Select All"; + this.mnuEditSelectAll.Click += new System.EventHandler(this.EditSelectAll_Click); + // + // mnuView + // + this.mnuView.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuViewDrawRapids, + this.mnuViewDrawBounds, + this.toolStripMenuItem5, + this.mnuViewZoomTo, + this.mnuViewZoomIn, + this.mnuViewZoomOut}); + this.mnuView.Name = "mnuView"; + this.mnuView.Size = new System.Drawing.Size(44, 20); + this.mnuView.Text = "&View"; + // + // mnuViewDrawRapids + // + this.mnuViewDrawRapids.Name = "mnuViewDrawRapids"; + this.mnuViewDrawRapids.Size = new System.Drawing.Size(222, 22); + this.mnuViewDrawRapids.Text = "Draw Rapids"; + this.mnuViewDrawRapids.Click += new System.EventHandler(this.ToggleDrawRapids_Click); + // + // mnuViewDrawBounds + // + this.mnuViewDrawBounds.CheckOnClick = true; + this.mnuViewDrawBounds.Name = "mnuViewDrawBounds"; + this.mnuViewDrawBounds.Size = new System.Drawing.Size(222, 22); + this.mnuViewDrawBounds.Text = "Draw Bounds"; + this.mnuViewDrawBounds.Click += new System.EventHandler(this.ToggleDrawBounds_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(219, 6); + // + // mnuViewZoomTo + // + this.mnuViewZoomTo.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuViewZoomToArea, + this.mnuViewZoomToFit, + this.mnuViewZoomToPlate, + this.mnuViewZoomToSelected}); + this.mnuViewZoomTo.Name = "mnuViewZoomTo"; + this.mnuViewZoomTo.Size = new System.Drawing.Size(222, 22); + this.mnuViewZoomTo.Text = "Zoom To"; + // + // mnuViewZoomToArea + // + this.mnuViewZoomToArea.Name = "mnuViewZoomToArea"; + this.mnuViewZoomToArea.Size = new System.Drawing.Size(118, 22); + this.mnuViewZoomToArea.Text = "Area"; + this.mnuViewZoomToArea.Click += new System.EventHandler(this.ZoomToArea_Click); + // + // mnuViewZoomToFit + // + this.mnuViewZoomToFit.Image = global::OpenNest.Properties.Resources.zoom_all; + this.mnuViewZoomToFit.Name = "mnuViewZoomToFit"; + this.mnuViewZoomToFit.Size = new System.Drawing.Size(118, 22); + this.mnuViewZoomToFit.Text = "Fit"; + this.mnuViewZoomToFit.Click += new System.EventHandler(this.ZoomToFit_Click); + // + // mnuViewZoomToPlate + // + this.mnuViewZoomToPlate.Name = "mnuViewZoomToPlate"; + this.mnuViewZoomToPlate.Size = new System.Drawing.Size(118, 22); + this.mnuViewZoomToPlate.Text = "Plate"; + this.mnuViewZoomToPlate.Click += new System.EventHandler(this.ZoomToPlate_Click); + // + // mnuViewZoomToSelected + // + this.mnuViewZoomToSelected.Name = "mnuViewZoomToSelected"; + this.mnuViewZoomToSelected.Size = new System.Drawing.Size(118, 22); + this.mnuViewZoomToSelected.Text = "Selected"; + this.mnuViewZoomToSelected.Click += new System.EventHandler(this.ZoomToSelected_Click); + // + // mnuViewZoomIn + // + this.mnuViewZoomIn.Image = global::OpenNest.Properties.Resources.zoom_in; + this.mnuViewZoomIn.Name = "mnuViewZoomIn"; + this.mnuViewZoomIn.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Oemplus))); + this.mnuViewZoomIn.Size = new System.Drawing.Size(222, 22); + this.mnuViewZoomIn.Text = "Zoom In"; + this.mnuViewZoomIn.Click += new System.EventHandler(this.ZoomIn_Click); + // + // mnuViewZoomOut + // + this.mnuViewZoomOut.Image = global::OpenNest.Properties.Resources.zoom_out; + this.mnuViewZoomOut.Name = "mnuViewZoomOut"; + this.mnuViewZoomOut.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.OemMinus))); + this.mnuViewZoomOut.Size = new System.Drawing.Size(222, 22); + this.mnuViewZoomOut.Text = "Zoom Out"; + this.mnuViewZoomOut.Click += new System.EventHandler(this.ZoomOut_Click); + // + // mnuTools + // + this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuToolsMeasureArea, + this.mnuToolsAlign, + this.toolStripMenuItem14, + this.mnuSetOffsetIncrement, + this.mnuSetRotationIncrement, + this.toolStripMenuItem15, + this.mnuToolsOptions}); + this.mnuTools.Name = "mnuTools"; + this.mnuTools.Size = new System.Drawing.Size(47, 20); + this.mnuTools.Text = "&Tools"; + // + // mnuToolsMeasureArea + // + this.mnuToolsMeasureArea.Name = "mnuToolsMeasureArea"; + this.mnuToolsMeasureArea.Size = new System.Drawing.Size(214, 22); + this.mnuToolsMeasureArea.Text = "Measure Area"; + this.mnuToolsMeasureArea.Click += new System.EventHandler(this.MeasureArea_Click); + // + // mnuToolsAlign + // + this.mnuToolsAlign.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuToolsAlignLeft, + this.mnuToolsAlignRight, + this.mnuToolsAlignTop, + this.mnuToolsAlignBottom, + this.toolStripMenuItem11, + this.mnuToolsAlignHorizontal, + this.mnuToolsAlignVertically, + this.toolStripMenuItem8, + this.mnuToolsEvenlySpaceHorizontal, + this.mnuToolsEvenlySpaceVertical}); + this.mnuToolsAlign.Name = "mnuToolsAlign"; + this.mnuToolsAlign.Size = new System.Drawing.Size(214, 22); + this.mnuToolsAlign.Text = "Align Selected"; + // + // mnuToolsAlignLeft + // + this.mnuToolsAlignLeft.Name = "mnuToolsAlignLeft"; + this.mnuToolsAlignLeft.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignLeft.Text = "Left"; + this.mnuToolsAlignLeft.Click += new System.EventHandler(this.AlignLeft_Click); + // + // mnuToolsAlignRight + // + this.mnuToolsAlignRight.Name = "mnuToolsAlignRight"; + this.mnuToolsAlignRight.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignRight.Text = "Right"; + this.mnuToolsAlignRight.Click += new System.EventHandler(this.AlignRight_Click); + // + // mnuToolsAlignTop + // + this.mnuToolsAlignTop.Name = "mnuToolsAlignTop"; + this.mnuToolsAlignTop.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignTop.Text = "Top"; + this.mnuToolsAlignTop.Click += new System.EventHandler(this.AlignTop_Click); + // + // mnuToolsAlignBottom + // + this.mnuToolsAlignBottom.Name = "mnuToolsAlignBottom"; + this.mnuToolsAlignBottom.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignBottom.Text = "Bottom"; + this.mnuToolsAlignBottom.Click += new System.EventHandler(this.AlignBottom_Click); + // + // toolStripMenuItem11 + // + this.toolStripMenuItem11.Name = "toolStripMenuItem11"; + this.toolStripMenuItem11.Size = new System.Drawing.Size(206, 6); + // + // mnuToolsAlignHorizontal + // + this.mnuToolsAlignHorizontal.Name = "mnuToolsAlignHorizontal"; + this.mnuToolsAlignHorizontal.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignHorizontal.Text = "Horizontally"; + this.mnuToolsAlignHorizontal.Click += new System.EventHandler(this.AlignHorizontal_Click); + // + // mnuToolsAlignVertically + // + this.mnuToolsAlignVertically.Name = "mnuToolsAlignVertically"; + this.mnuToolsAlignVertically.Size = new System.Drawing.Size(209, 22); + this.mnuToolsAlignVertically.Text = "Vertically"; + this.mnuToolsAlignVertically.Click += new System.EventHandler(this.AlignVertical_Click); + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(206, 6); + // + // mnuToolsEvenlySpaceHorizontal + // + this.mnuToolsEvenlySpaceHorizontal.Name = "mnuToolsEvenlySpaceHorizontal"; + this.mnuToolsEvenlySpaceHorizontal.Size = new System.Drawing.Size(209, 22); + this.mnuToolsEvenlySpaceHorizontal.Text = "Evenly Space Horizontally"; + this.mnuToolsEvenlySpaceHorizontal.Click += new System.EventHandler(this.EvenlySpaceHorizontally_Click); + // + // mnuToolsEvenlySpaceVertical + // + this.mnuToolsEvenlySpaceVertical.Name = "mnuToolsEvenlySpaceVertical"; + this.mnuToolsEvenlySpaceVertical.Size = new System.Drawing.Size(209, 22); + this.mnuToolsEvenlySpaceVertical.Text = "Evenly Space Vertically"; + this.mnuToolsEvenlySpaceVertical.Click += new System.EventHandler(this.EvenlySpaceVertically_Click); + // + // toolStripMenuItem14 + // + this.toolStripMenuItem14.Name = "toolStripMenuItem14"; + this.toolStripMenuItem14.Size = new System.Drawing.Size(211, 6); + // + // mnuSetOffsetIncrement + // + this.mnuSetOffsetIncrement.Name = "mnuSetOffsetIncrement"; + this.mnuSetOffsetIncrement.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.mnuSetOffsetIncrement.Size = new System.Drawing.Size(214, 22); + this.mnuSetOffsetIncrement.Text = "Set Offset Increment"; + this.mnuSetOffsetIncrement.Click += new System.EventHandler(this.SetOffsetIncrement_Click); + // + // mnuSetRotationIncrement + // + this.mnuSetRotationIncrement.Name = "mnuSetRotationIncrement"; + this.mnuSetRotationIncrement.ShortcutKeys = System.Windows.Forms.Keys.F6; + this.mnuSetRotationIncrement.Size = new System.Drawing.Size(214, 22); + this.mnuSetRotationIncrement.Text = "Set Rotation Increment"; + this.mnuSetRotationIncrement.Click += new System.EventHandler(this.SetRotationIncrement_Click); + // + // toolStripMenuItem15 + // + this.toolStripMenuItem15.Name = "toolStripMenuItem15"; + this.toolStripMenuItem15.Size = new System.Drawing.Size(211, 6); + // + // mnuToolsOptions + // + this.mnuToolsOptions.Name = "mnuToolsOptions"; + this.mnuToolsOptions.Size = new System.Drawing.Size(214, 22); + this.mnuToolsOptions.Text = "Options"; + this.mnuToolsOptions.Click += new System.EventHandler(this.Options_Click); + // + // mnuNest + // + this.mnuNest.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuNestEdit, + this.mnuNestImportDrawing, + this.toolStripMenuItem7, + this.mnuNestFirstPlate, + this.mnuNestLastPlate, + this.toolStripMenuItem6, + this.mnuNestNextPlate, + this.mnuNestPreviousPlate, + this.toolStripMenuItem12, + this.runAutoNestToolStripMenuItem, + this.autoSequenceAllPlatesToolStripMenuItem, + this.mnuNestRemoveEmptyPlates, + this.mnuNestPost, + this.toolStripMenuItem19, + this.calculateCutTimeToolStripMenuItem}); + this.mnuNest.Name = "mnuNest"; + this.mnuNest.Size = new System.Drawing.Size(43, 20); + this.mnuNest.Text = "&Nest"; + // + // mnuNestEdit + // + this.mnuNestEdit.Name = "mnuNestEdit"; + this.mnuNestEdit.Size = new System.Drawing.Size(205, 22); + this.mnuNestEdit.Text = "Edit"; + this.mnuNestEdit.Click += new System.EventHandler(this.EditNest_Click); + // + // mnuNestImportDrawing + // + this.mnuNestImportDrawing.Image = global::OpenNest.Properties.Resources.import; + this.mnuNestImportDrawing.Name = "mnuNestImportDrawing"; + this.mnuNestImportDrawing.Size = new System.Drawing.Size(205, 22); + this.mnuNestImportDrawing.Text = "Import Drawing"; + this.mnuNestImportDrawing.Click += new System.EventHandler(this.Import_Click); + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(202, 6); + // + // mnuNestFirstPlate + // + this.mnuNestFirstPlate.Image = global::OpenNest.Properties.Resources.move_first; + this.mnuNestFirstPlate.Name = "mnuNestFirstPlate"; + this.mnuNestFirstPlate.Size = new System.Drawing.Size(205, 22); + this.mnuNestFirstPlate.Text = "First Plate"; + this.mnuNestFirstPlate.Click += new System.EventHandler(this.LoadFirstPlate_Click); + // + // mnuNestLastPlate + // + this.mnuNestLastPlate.Image = global::OpenNest.Properties.Resources.move_last; + this.mnuNestLastPlate.Name = "mnuNestLastPlate"; + this.mnuNestLastPlate.Size = new System.Drawing.Size(205, 22); + this.mnuNestLastPlate.Text = "Last Plate"; + this.mnuNestLastPlate.Click += new System.EventHandler(this.LoadLastPlate_Click); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(202, 6); + // + // mnuNestNextPlate + // + this.mnuNestNextPlate.Image = global::OpenNest.Properties.Resources.move_next; + this.mnuNestNextPlate.Name = "mnuNestNextPlate"; + this.mnuNestNextPlate.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Right))); + this.mnuNestNextPlate.Size = new System.Drawing.Size(205, 22); + this.mnuNestNextPlate.Text = "Next Plate"; + this.mnuNestNextPlate.Click += new System.EventHandler(this.LoadNextPlate_Click); + // + // mnuNestPreviousPlate + // + this.mnuNestPreviousPlate.Image = global::OpenNest.Properties.Resources.move_previous; + this.mnuNestPreviousPlate.Name = "mnuNestPreviousPlate"; + this.mnuNestPreviousPlate.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Left))); + this.mnuNestPreviousPlate.Size = new System.Drawing.Size(205, 22); + this.mnuNestPreviousPlate.Text = "Previous Plate"; + this.mnuNestPreviousPlate.Click += new System.EventHandler(this.LoadPreviousPlate_Click); + // + // toolStripMenuItem12 + // + this.toolStripMenuItem12.Name = "toolStripMenuItem12"; + this.toolStripMenuItem12.Size = new System.Drawing.Size(202, 6); + // + // runAutoNestToolStripMenuItem + // + this.runAutoNestToolStripMenuItem.Name = "runAutoNestToolStripMenuItem"; + this.runAutoNestToolStripMenuItem.Size = new System.Drawing.Size(205, 22); + this.runAutoNestToolStripMenuItem.Text = "Auto Nest"; + this.runAutoNestToolStripMenuItem.Click += new System.EventHandler(this.RunAutoNest_Click); + // + // autoSequenceAllPlatesToolStripMenuItem + // + this.autoSequenceAllPlatesToolStripMenuItem.Name = "autoSequenceAllPlatesToolStripMenuItem"; + this.autoSequenceAllPlatesToolStripMenuItem.Size = new System.Drawing.Size(205, 22); + this.autoSequenceAllPlatesToolStripMenuItem.Text = "Auto Sequence All Plates"; + this.autoSequenceAllPlatesToolStripMenuItem.Click += new System.EventHandler(this.SequenceAllPlates_Click); + // + // mnuNestRemoveEmptyPlates + // + this.mnuNestRemoveEmptyPlates.Name = "mnuNestRemoveEmptyPlates"; + this.mnuNestRemoveEmptyPlates.Size = new System.Drawing.Size(205, 22); + this.mnuNestRemoveEmptyPlates.Text = "Remove Empty Plates"; + this.mnuNestRemoveEmptyPlates.Click += new System.EventHandler(this.RemoveEmptyPlates_Click); + // + // mnuNestPost + // + this.mnuNestPost.Name = "mnuNestPost"; + this.mnuNestPost.Size = new System.Drawing.Size(205, 22); + this.mnuNestPost.Text = "Post"; + // + // toolStripMenuItem19 + // + this.toolStripMenuItem19.Name = "toolStripMenuItem19"; + this.toolStripMenuItem19.Size = new System.Drawing.Size(202, 6); + // + // calculateCutTimeToolStripMenuItem + // + this.calculateCutTimeToolStripMenuItem.Name = "calculateCutTimeToolStripMenuItem"; + this.calculateCutTimeToolStripMenuItem.Size = new System.Drawing.Size(205, 22); + this.calculateCutTimeToolStripMenuItem.Text = "Calculate Cut Time"; + this.calculateCutTimeToolStripMenuItem.Click += new System.EventHandler(this.CalculateNestCutTime_Click); + // + // mnuPlate + // + this.mnuPlate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuPlateEdit, + this.mnuPlateSetAsDefault, + this.toolStripMenuItem18, + this.mnuPlateAdd, + this.mnuPlateRemove, + this.toolStripMenuItem16, + this.mnuPlateFill, + this.toolStripMenuItem9, + this.mnuPlateRotate, + this.mnuResizeToFitParts, + this.toolStripMenuItem13, + this.mnuPlateViewInCad, + this.toolStripMenuItem20, + this.mnuSequenceParts, + this.calculateCutTimeToolStripMenuItem1, + this.centerPartsToolStripMenuItem}); + this.mnuPlate.Name = "mnuPlate"; + this.mnuPlate.Size = new System.Drawing.Size(45, 20); + this.mnuPlate.Text = "&Plate"; + // + // mnuPlateEdit + // + this.mnuPlateEdit.Name = "mnuPlateEdit"; + this.mnuPlateEdit.Size = new System.Drawing.Size(177, 22); + this.mnuPlateEdit.Text = "Edit"; + this.mnuPlateEdit.Click += new System.EventHandler(this.EditPlate_Click); + // + // mnuPlateSetAsDefault + // + this.mnuPlateSetAsDefault.Name = "mnuPlateSetAsDefault"; + this.mnuPlateSetAsDefault.Size = new System.Drawing.Size(177, 22); + this.mnuPlateSetAsDefault.Text = "Set as Default"; + this.mnuPlateSetAsDefault.Click += new System.EventHandler(this.SetAsNestDefault_Click); + // + // toolStripMenuItem18 + // + this.toolStripMenuItem18.Name = "toolStripMenuItem18"; + this.toolStripMenuItem18.Size = new System.Drawing.Size(174, 6); + // + // mnuPlateAdd + // + this.mnuPlateAdd.Image = global::OpenNest.Properties.Resources.add; + this.mnuPlateAdd.Name = "mnuPlateAdd"; + this.mnuPlateAdd.Size = new System.Drawing.Size(177, 22); + this.mnuPlateAdd.Text = "Add"; + this.mnuPlateAdd.Click += new System.EventHandler(this.AddPlate_Click); + // + // mnuPlateRemove + // + this.mnuPlateRemove.Image = global::OpenNest.Properties.Resources.remove; + this.mnuPlateRemove.Name = "mnuPlateRemove"; + this.mnuPlateRemove.Size = new System.Drawing.Size(177, 22); + this.mnuPlateRemove.Text = "Remove"; + this.mnuPlateRemove.Click += new System.EventHandler(this.RemovePlate_Click); + // + // toolStripMenuItem16 + // + this.toolStripMenuItem16.Name = "toolStripMenuItem16"; + this.toolStripMenuItem16.Size = new System.Drawing.Size(174, 6); + // + // mnuPlateFill + // + this.mnuPlateFill.Name = "mnuPlateFill"; + this.mnuPlateFill.Size = new System.Drawing.Size(177, 22); + this.mnuPlateFill.Text = "Fill"; + this.mnuPlateFill.Click += new System.EventHandler(this.FillPlate_Click); + // + // toolStripMenuItem9 + // + this.toolStripMenuItem9.Name = "toolStripMenuItem9"; + this.toolStripMenuItem9.Size = new System.Drawing.Size(177, 22); + this.toolStripMenuItem9.Text = "Fill Area"; + this.toolStripMenuItem9.Click += new System.EventHandler(this.FillArea_Click); + // + // mnuPlateRotate + // + this.mnuPlateRotate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuPlateRotateCw, + this.mnuPlateRotateCcw, + this.toolStripSeparator2, + this.mnuPlateRotate180}); + this.mnuPlateRotate.Name = "mnuPlateRotate"; + this.mnuPlateRotate.Size = new System.Drawing.Size(177, 22); + this.mnuPlateRotate.Text = "Rotate"; + // + // mnuPlateRotateCw + // + this.mnuPlateRotateCw.Image = global::OpenNest.Properties.Resources.rotate_cw; + this.mnuPlateRotateCw.Name = "mnuPlateRotateCw"; + this.mnuPlateRotateCw.Size = new System.Drawing.Size(121, 22); + this.mnuPlateRotateCw.Text = "90° CW"; + this.mnuPlateRotateCw.Click += new System.EventHandler(this.RotateCw_Click); + // + // mnuPlateRotateCcw + // + this.mnuPlateRotateCcw.Image = global::OpenNest.Properties.Resources.rotate_ccw; + this.mnuPlateRotateCcw.Name = "mnuPlateRotateCcw"; + this.mnuPlateRotateCcw.Size = new System.Drawing.Size(121, 22); + this.mnuPlateRotateCcw.Text = "90° CCW"; + this.mnuPlateRotateCcw.Click += new System.EventHandler(this.RotateCcw_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(118, 6); + // + // mnuPlateRotate180 + // + this.mnuPlateRotate180.Name = "mnuPlateRotate180"; + this.mnuPlateRotate180.Size = new System.Drawing.Size(121, 22); + this.mnuPlateRotate180.Text = "180°"; + this.mnuPlateRotate180.Click += new System.EventHandler(this.Rotate180_Click); + // + // mnuResizeToFitParts + // + this.mnuResizeToFitParts.Name = "mnuResizeToFitParts"; + this.mnuResizeToFitParts.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.P))); + this.mnuResizeToFitParts.Size = new System.Drawing.Size(177, 22); + this.mnuResizeToFitParts.Text = "Resize to Fit"; + this.mnuResizeToFitParts.Click += new System.EventHandler(this.ResizeToFitParts_Click); + // + // toolStripMenuItem13 + // + this.toolStripMenuItem13.Name = "toolStripMenuItem13"; + this.toolStripMenuItem13.Size = new System.Drawing.Size(174, 6); + // + // mnuPlateViewInCad + // + this.mnuPlateViewInCad.Name = "mnuPlateViewInCad"; + this.mnuPlateViewInCad.Size = new System.Drawing.Size(177, 22); + this.mnuPlateViewInCad.Text = "View in CAD"; + this.mnuPlateViewInCad.Click += new System.EventHandler(this.OpenInExternalCad_Click); + // + // toolStripMenuItem20 + // + this.toolStripMenuItem20.Name = "toolStripMenuItem20"; + this.toolStripMenuItem20.Size = new System.Drawing.Size(174, 6); + // + // mnuSequenceParts + // + this.mnuSequenceParts.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.autoSequenceCurrentPlateToolStripMenuItem, + this.manualSequenceToolStripMenuItem}); + this.mnuSequenceParts.Name = "mnuSequenceParts"; + this.mnuSequenceParts.Size = new System.Drawing.Size(177, 22); + this.mnuSequenceParts.Text = "Sequence Parts"; + // + // autoSequenceCurrentPlateToolStripMenuItem + // + this.autoSequenceCurrentPlateToolStripMenuItem.Name = "autoSequenceCurrentPlateToolStripMenuItem"; + this.autoSequenceCurrentPlateToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.autoSequenceCurrentPlateToolStripMenuItem.Text = "Auto Sequence"; + this.autoSequenceCurrentPlateToolStripMenuItem.Click += new System.EventHandler(this.AutoSequenceCurrentPlate_Click); + // + // manualSequenceToolStripMenuItem + // + this.manualSequenceToolStripMenuItem.Name = "manualSequenceToolStripMenuItem"; + this.manualSequenceToolStripMenuItem.Size = new System.Drawing.Size(168, 22); + this.manualSequenceToolStripMenuItem.Text = "Manual Sequence"; + this.manualSequenceToolStripMenuItem.Click += new System.EventHandler(this.ManualSequenceParts_Click); + // + // calculateCutTimeToolStripMenuItem1 + // + this.calculateCutTimeToolStripMenuItem1.Name = "calculateCutTimeToolStripMenuItem1"; + this.calculateCutTimeToolStripMenuItem1.Size = new System.Drawing.Size(177, 22); + this.calculateCutTimeToolStripMenuItem1.Text = "Calculate Cut Time"; + this.calculateCutTimeToolStripMenuItem1.Click += new System.EventHandler(this.CalculatePlateCutTime_Click); + // + // centerPartsToolStripMenuItem + // + this.centerPartsToolStripMenuItem.Name = "centerPartsToolStripMenuItem"; + this.centerPartsToolStripMenuItem.Size = new System.Drawing.Size(177, 22); + this.centerPartsToolStripMenuItem.Text = "Center Parts"; + this.centerPartsToolStripMenuItem.Click += new System.EventHandler(this.centerPartsToolStripMenuItem_Click); + // + // mnuWindow + // + this.mnuWindow.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuWindowCascade, + this.mnuWindowTileVertical, + this.mnuWindowTileHorizontal, + this.toolStripMenuItem10, + this.closeToolStripMenuItem, + this.mnuCloseAll}); + this.mnuWindow.Name = "mnuWindow"; + this.mnuWindow.Size = new System.Drawing.Size(63, 20); + this.mnuWindow.Text = "&Window"; + // + // mnuWindowCascade + // + this.mnuWindowCascade.Name = "mnuWindowCascade"; + this.mnuWindowCascade.Size = new System.Drawing.Size(151, 22); + this.mnuWindowCascade.Text = "Cascade"; + this.mnuWindowCascade.Click += new System.EventHandler(this.CascadeWindows_Click); + // + // mnuWindowTileVertical + // + this.mnuWindowTileVertical.Name = "mnuWindowTileVertical"; + this.mnuWindowTileVertical.Size = new System.Drawing.Size(151, 22); + this.mnuWindowTileVertical.Text = "Tile Vertical"; + this.mnuWindowTileVertical.Click += new System.EventHandler(this.TileVertical_Click); + // + // mnuWindowTileHorizontal + // + this.mnuWindowTileHorizontal.Name = "mnuWindowTileHorizontal"; + this.mnuWindowTileHorizontal.Size = new System.Drawing.Size(151, 22); + this.mnuWindowTileHorizontal.Text = "Tile Horizontal"; + this.mnuWindowTileHorizontal.Click += new System.EventHandler(this.TileHorizontal_Click); + // + // toolStripMenuItem10 + // + this.toolStripMenuItem10.Name = "toolStripMenuItem10"; + this.toolStripMenuItem10.Size = new System.Drawing.Size(148, 6); + // + // closeToolStripMenuItem + // + this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; + this.closeToolStripMenuItem.Size = new System.Drawing.Size(151, 22); + this.closeToolStripMenuItem.Text = "Close"; + this.closeToolStripMenuItem.Click += new System.EventHandler(this.Close_Click); + // + // mnuCloseAll + // + this.mnuCloseAll.Name = "mnuCloseAll"; + this.mnuCloseAll.Size = new System.Drawing.Size(151, 22); + this.mnuCloseAll.Text = "Close All"; + this.mnuCloseAll.Click += new System.EventHandler(this.CloseAll_Click); + // + // statusStrip1 + // + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.statusLabel1, + this.locationStatusLabel, + this.spacerLabel, + this.plateIndexStatusLabel, + this.plateSizeStatusLabel, + this.plateQtyStatusLabel}); + this.statusStrip1.Location = new System.Drawing.Point(0, 543); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(1098, 24); + this.statusStrip1.TabIndex = 9; + this.statusStrip1.Text = "statusStrip1"; + // + // statusLabel1 + // + this.statusLabel1.Name = "statusLabel1"; + this.statusLabel1.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.statusLabel1.Size = new System.Drawing.Size(10, 19); + // + // locationStatusLabel + // + this.locationStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + this.locationStatusLabel.Name = "locationStatusLabel"; + this.locationStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.locationStatusLabel.Size = new System.Drawing.Size(102, 19); + this.locationStatusLabel.Text = "Location : [0, 0]"; + this.locationStatusLabel.Click += new System.EventHandler(this.LocationStatusLabel_Click); + // + // spacerLabel + // + this.spacerLabel.Name = "spacerLabel"; + this.spacerLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.spacerLabel.Size = new System.Drawing.Size(764, 19); + this.spacerLabel.Spring = true; + // + // plateIndexStatusLabel + // + this.plateIndexStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + this.plateIndexStatusLabel.Name = "plateIndexStatusLabel"; + this.plateIndexStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.plateIndexStatusLabel.Size = new System.Drawing.Size(85, 19); + this.plateIndexStatusLabel.Text = "Plate : 0 of 0"; + // + // plateSizeStatusLabel + // + this.plateSizeStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + this.plateSizeStatusLabel.Name = "plateSizeStatusLabel"; + this.plateSizeStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.plateSizeStatusLabel.Size = new System.Drawing.Size(67, 19); + this.plateSizeStatusLabel.Text = "Size : 0x0"; + // + // plateQtyStatusLabel + // + this.plateQtyStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + this.plateQtyStatusLabel.Name = "plateQtyStatusLabel"; + this.plateQtyStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.plateQtyStatusLabel.Size = new System.Drawing.Size(55, 19); + this.plateQtyStatusLabel.Text = "Qty : 0"; + // + // toolStrip1 + // + this.toolStrip1.AutoSize = false; + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btnNew, + this.btnOpen, + this.btnSave, + this.btnSaveAs, + this.toolStripSeparator1, + this.btnFirstPlate, + this.btnPreviousPlate, + this.btnNextPlate, + this.btnLastPlate, + this.toolStripSeparator3, + this.btnZoomOut, + this.btnZoomIn, + this.btnZoomToFit}); + this.toolStrip1.Location = new System.Drawing.Point(0, 24); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(1098, 35); + this.toolStrip1.TabIndex = 10; + this.toolStrip1.Text = "toolStrip1"; + // + // btnNew + // + this.btnNew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnNew.Image = global::OpenNest.Properties.Resources.doc_new; + this.btnNew.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnNew.Name = "btnNew"; + this.btnNew.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnNew.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnNew.Size = new System.Drawing.Size(38, 32); + this.btnNew.Text = "New"; + this.btnNew.Click += new System.EventHandler(this.New_Click); + // + // btnOpen + // + this.btnOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnOpen.Image = global::OpenNest.Properties.Resources.doc_open; + this.btnOpen.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnOpen.Name = "btnOpen"; + this.btnOpen.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnOpen.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnOpen.Size = new System.Drawing.Size(38, 32); + this.btnOpen.Text = "Open"; + this.btnOpen.Click += new System.EventHandler(this.Open_Click); + // + // btnSave + // + this.btnSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnSave.Image = global::OpenNest.Properties.Resources.save; + this.btnSave.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnSave.Name = "btnSave"; + this.btnSave.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnSave.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnSave.Size = new System.Drawing.Size(38, 32); + this.btnSave.Text = "Save"; + this.btnSave.Click += new System.EventHandler(this.Save_Click); + // + // btnSaveAs + // + this.btnSaveAs.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnSaveAs.Image = global::OpenNest.Properties.Resources.save_as; + this.btnSaveAs.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnSaveAs.Name = "btnSaveAs"; + this.btnSaveAs.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnSaveAs.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnSaveAs.Size = new System.Drawing.Size(38, 32); + this.btnSaveAs.Text = "Save As"; + this.btnSaveAs.Click += new System.EventHandler(this.SaveAs_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 35); + // + // btnFirstPlate + // + this.btnFirstPlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnFirstPlate.Image = global::OpenNest.Properties.Resources.move_first; + this.btnFirstPlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnFirstPlate.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnFirstPlate.Name = "btnFirstPlate"; + this.btnFirstPlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnFirstPlate.Size = new System.Drawing.Size(38, 32); + this.btnFirstPlate.Text = "Go to First Plate"; + this.btnFirstPlate.Click += new System.EventHandler(this.LoadFirstPlate_Click); + // + // btnPreviousPlate + // + this.btnPreviousPlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnPreviousPlate.Image = global::OpenNest.Properties.Resources.move_previous; + this.btnPreviousPlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnPreviousPlate.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnPreviousPlate.Name = "btnPreviousPlate"; + this.btnPreviousPlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnPreviousPlate.Size = new System.Drawing.Size(38, 32); + this.btnPreviousPlate.Text = "Go to Previous Plate"; + this.btnPreviousPlate.Click += new System.EventHandler(this.LoadPreviousPlate_Click); + // + // btnNextPlate + // + this.btnNextPlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnNextPlate.Image = global::OpenNest.Properties.Resources.move_next; + this.btnNextPlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnNextPlate.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnNextPlate.Name = "btnNextPlate"; + this.btnNextPlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnNextPlate.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnNextPlate.Size = new System.Drawing.Size(38, 32); + this.btnNextPlate.Text = "Go to Next Plate"; + this.btnNextPlate.Click += new System.EventHandler(this.LoadNextPlate_Click); + // + // btnLastPlate + // + this.btnLastPlate.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnLastPlate.Image = global::OpenNest.Properties.Resources.move_last; + this.btnLastPlate.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnLastPlate.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnLastPlate.Name = "btnLastPlate"; + this.btnLastPlate.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnLastPlate.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnLastPlate.Size = new System.Drawing.Size(38, 32); + this.btnLastPlate.Text = "Go to Last Plate"; + this.btnLastPlate.Click += new System.EventHandler(this.LoadLastPlate_Click); + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(6, 35); + // + // btnZoomOut + // + this.btnZoomOut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnZoomOut.Image = global::OpenNest.Properties.Resources.zoom_out; + this.btnZoomOut.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnZoomOut.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnZoomOut.Name = "btnZoomOut"; + this.btnZoomOut.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnZoomOut.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnZoomOut.Size = new System.Drawing.Size(38, 32); + this.btnZoomOut.Text = "Zoom Out"; + this.btnZoomOut.Click += new System.EventHandler(this.ZoomOut_Click); + // + // btnZoomIn + // + this.btnZoomIn.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnZoomIn.Image = global::OpenNest.Properties.Resources.zoom_in; + this.btnZoomIn.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnZoomIn.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnZoomIn.Name = "btnZoomIn"; + this.btnZoomIn.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnZoomIn.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnZoomIn.Size = new System.Drawing.Size(38, 32); + this.btnZoomIn.Text = "Zoom In"; + this.btnZoomIn.Click += new System.EventHandler(this.ZoomIn_Click); + // + // btnZoomToFit + // + this.btnZoomToFit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnZoomToFit.Image = global::OpenNest.Properties.Resources.zoom_all; + this.btnZoomToFit.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.btnZoomToFit.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnZoomToFit.Name = "btnZoomToFit"; + this.btnZoomToFit.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + this.btnZoomToFit.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.btnZoomToFit.Size = new System.Drawing.Size(38, 32); + this.btnZoomToFit.Text = "Zoom To Fit"; + this.btnZoomToFit.Click += new System.EventHandler(this.ZoomToFit_Click); + // + // pEPToolStripMenuItem + // + this.pEPToolStripMenuItem.Name = "pEPToolStripMenuItem"; + this.pEPToolStripMenuItem.Size = new System.Drawing.Size(32, 19); + // + // openNestToolStripMenuItem + // + this.openNestToolStripMenuItem.Name = "openNestToolStripMenuItem"; + this.openNestToolStripMenuItem.Size = new System.Drawing.Size(32, 19); + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackgroundImage = global::OpenNest.Properties.Resources.watermark; + this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.ClientSize = new System.Drawing.Size(1098, 567); + this.Controls.Add(this.toolStrip1); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.menuStrip1); + this.DoubleBuffered = true; + this.IsMdiContainer = true; + this.MainMenuStrip = this.menuStrip1; + this.MinimumSize = new System.Drawing.Size(506, 335); + this.Name = "MainForm"; + this.Text = "OpenNest"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem mnuFile; + private System.Windows.Forms.ToolStripMenuItem mnuFileNew; + private System.Windows.Forms.ToolStripMenuItem mnuFileOpen; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuFileSave; + private System.Windows.Forms.ToolStripMenuItem mnuFileSaveAs; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem mnuFileExport; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem mnuFileExit; + private System.Windows.Forms.ToolStripMenuItem mnuEdit; + private System.Windows.Forms.ToolStripMenuItem mnuEditCut; + private System.Windows.Forms.ToolStripMenuItem mnuEditCopy; + private System.Windows.Forms.ToolStripMenuItem mnuEditPaste; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + private System.Windows.Forms.ToolStripMenuItem mnuEditSelectAll; + private System.Windows.Forms.ToolStripMenuItem mnuView; + private System.Windows.Forms.ToolStripMenuItem mnuViewDrawRapids; + private System.Windows.Forms.ToolStripMenuItem mnuViewDrawBounds; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + private System.Windows.Forms.ToolStripMenuItem mnuTools; + private System.Windows.Forms.ToolStripMenuItem mnuToolsOptions; + private System.Windows.Forms.ToolStripMenuItem mnuNest; + private System.Windows.Forms.ToolStripMenuItem mnuNestEdit; + private System.Windows.Forms.ToolStripMenuItem mnuNestImportDrawing; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem mnuNestFirstPlate; + private System.Windows.Forms.ToolStripMenuItem mnuNestLastPlate; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6; + private System.Windows.Forms.ToolStripMenuItem mnuNestNextPlate; + private System.Windows.Forms.ToolStripMenuItem mnuNestPreviousPlate; + private System.Windows.Forms.ToolStripMenuItem mnuPlate; + private System.Windows.Forms.ToolStripMenuItem mnuPlateEdit; + private System.Windows.Forms.ToolStripMenuItem mnuPlateRotate; + private System.Windows.Forms.ToolStripMenuItem mnuPlateRotateCw; + private System.Windows.Forms.ToolStripMenuItem mnuPlateRotateCcw; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripMenuItem mnuPlateRotate180; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStripStatusLabel plateSizeStatusLabel; + private System.Windows.Forms.ToolStripStatusLabel locationStatusLabel; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.ToolStripButton btnOpen; + private System.Windows.Forms.ToolStripStatusLabel plateIndexStatusLabel; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripButton btnPreviousPlate; + private System.Windows.Forms.ToolStripButton btnNextPlate; + private System.Windows.Forms.ToolStripMenuItem mnuWindow; + private System.Windows.Forms.ToolStripMenuItem mnuWindowCascade; + private System.Windows.Forms.ToolStripMenuItem mnuWindowTileVertical; + private System.Windows.Forms.ToolStripMenuItem mnuWindowTileHorizontal; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10; + private System.Windows.Forms.ToolStripMenuItem mnuCloseAll; + private System.Windows.Forms.ToolStripStatusLabel plateQtyStatusLabel; + private System.Windows.Forms.ToolStripMenuItem mnuFileExportAll; + private System.Windows.Forms.ToolStripMenuItem openNestToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem pEPToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomTo; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToArea; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToFit; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToPlate; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToSelected; + private System.Windows.Forms.ToolStripMenuItem mnuResizeToFitParts; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlign; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignLeft; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignRight; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignTop; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignBottom; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignHorizontal; + private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignVertically; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; + private System.Windows.Forms.ToolStripMenuItem mnuToolsEvenlySpaceHorizontal; + private System.Windows.Forms.ToolStripMenuItem mnuToolsEvenlySpaceVertical; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12; + private System.Windows.Forms.ToolStripMenuItem mnuNestPost; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem13; + private System.Windows.Forms.ToolStripMenuItem mnuPlateViewInCad; + private System.Windows.Forms.ToolStripButton btnNew; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAdd; + private System.Windows.Forms.ToolStripButton btnFirstPlate; + private System.Windows.Forms.ToolStripButton btnLastPlate; + private System.Windows.Forms.ToolStripButton btnZoomToFit; + private System.Windows.Forms.ToolStripMenuItem mnuNestRemoveEmptyPlates; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomIn; + private System.Windows.Forms.ToolStripMenuItem mnuViewZoomOut; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem14; + private System.Windows.Forms.ToolStripMenuItem mnuSetOffsetIncrement; + private System.Windows.Forms.ToolStripMenuItem mnuSetRotationIncrement; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem15; + private System.Windows.Forms.ToolStripMenuItem mnuPlateSetAsDefault; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem16; + private System.Windows.Forms.ToolStripButton btnZoomOut; + private System.Windows.Forms.ToolStripButton btnZoomIn; + private System.Windows.Forms.ToolStripStatusLabel statusLabel1; + private System.Windows.Forms.ToolStripStatusLabel spacerLabel; + private System.Windows.Forms.ToolStripMenuItem mnuPlateFill; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem18; + private System.Windows.Forms.ToolStripMenuItem mnuPlateRemove; + private System.Windows.Forms.ToolStripButton btnSave; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem19; + private System.Windows.Forms.ToolStripMenuItem calculateCutTimeToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem20; + private System.Windows.Forms.ToolStripMenuItem calculateCutTimeToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuSequenceParts; + private System.Windows.Forms.ToolStripMenuItem closeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem runAutoNestToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem9; + private System.Windows.Forms.ToolStripMenuItem autoSequenceCurrentPlateToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem manualSequenceToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem autoSequenceAllPlatesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuToolsMeasureArea; + private System.Windows.Forms.ToolStripButton btnSaveAs; + private System.Windows.Forms.ToolStripMenuItem centerPartsToolStripMenuItem; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/MainForm.cs b/Source/OpenNest/Forms/MainForm.cs new file mode 100644 index 0000000..de99a04 --- /dev/null +++ b/Source/OpenNest/Forms/MainForm.cs @@ -0,0 +1,891 @@ +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; +using OpenNest.Actions; +using OpenNest.IO; +using OpenNest.Properties; + +namespace OpenNest.Forms +{ + public partial class MainForm : Form + { + private EditNestForm activeForm; + private bool clickUpdateLocation; + + private const float ZoomInFactor = 1.5f; + private const float ZoomOutFactor = 1.0f / ZoomInFactor; + + public MainForm() + { + InitializeComponent(); + LoadSettings(); + + var renderer = new ToolStripRenderer(ToolbarTheme.Toolbar); + menuStrip1.Renderer = renderer; + toolStrip1.Renderer = renderer; + + mnuViewZoomIn.ShortcutKeyDisplayString = "Ctrl+Plus"; + mnuViewZoomOut.ShortcutKeyDisplayString = "Ctrl+Minus"; + + clickUpdateLocation = true; + + this.SetBevel(false); + + LoadPosts(); + EnableCheck(); + UpdateStatus(); + } + + private string GetNestName(DateTime date, int id) + { + var month = date.Month.ToString().PadLeft(2, '0'); + var day = date.Day.ToString().PadLeft(2, '0'); + + return string.Format("N{0}{1}-{2}", month, day, id.ToString().PadLeft(3, '0')); + } + + private void LoadNest(Nest nest, FormWindowState windowState = FormWindowState.Maximized) + { + var editForm = new EditNestForm(nest); + editForm.MdiParent = this; + editForm.PlateChanged += (sender, e) => + { + NavigationEnableCheck(); + UpdatePlateStatus(); + }; + editForm.WindowState = windowState; + editForm.Show(); + editForm.PlateView.ZoomToFit(); + } + + private void LoadSettings() + { + var screen = Screen.PrimaryScreen.Bounds; + + if (screen.Contains(Settings.Default.MainWindowLocation)) + Location = Settings.Default.MainWindowLocation; + + if (Settings.Default.MainWindowSize.Width <= screen.Width && + Settings.Default.MainWindowSize.Height <= screen.Height) + { + Size = Settings.Default.MainWindowSize; + } + + WindowState = Settings.Default.MainWindowState; + } + + private void SaveSettings() + { + Settings.Default.MainWindowLocation = Location; + Settings.Default.MainWindowSize = Size; + Settings.Default.MainWindowState = WindowState; + Settings.Default.Save(); + } + + private void EnableCheck() + { + NavigationEnableCheck(); + + var hasValue = activeForm != null; + + btnZoomToFit.Enabled = hasValue; + mnuFileSave.Enabled = hasValue; + btnSave.Enabled = hasValue; + btnSaveAs.Enabled = hasValue; + mnuFileSaveAs.Enabled = hasValue; + mnuFileExport.Enabled = hasValue; + mnuFileExportAll.Enabled = hasValue; + btnZoomOut.Enabled = hasValue; + btnZoomIn.Enabled = hasValue; + mnuEdit.Visible = hasValue; + mnuView.Visible = hasValue; + mnuNest.Visible = hasValue; + mnuPlate.Visible = hasValue; + mnuWindow.Visible = hasValue; + mnuToolsAlign.Visible = hasValue; + mnuToolsMeasureArea.Visible = hasValue; + + toolStripMenuItem14.Visible = hasValue; + mnuSetOffsetIncrement.Visible = hasValue; + mnuSetRotationIncrement.Visible = hasValue; + toolStripMenuItem15.Visible = hasValue; + } + + private void NavigationEnableCheck() + { + if (activeForm == null) + { + mnuNestPreviousPlate.Enabled = false; + btnPreviousPlate.Enabled = false; + + mnuNestNextPlate.Enabled = false; + btnNextPlate.Enabled = false; + + mnuNestFirstPlate.Enabled = false; + btnFirstPlate.Enabled = false; + + mnuNestLastPlate.Enabled = false; + btnLastPlate.Enabled = false; + } + else + { + mnuNestPreviousPlate.Enabled = !activeForm.IsFirstPlate(); + btnPreviousPlate.Enabled = !activeForm.IsFirstPlate(); + + mnuNestNextPlate.Enabled = !activeForm.IsLastPlate(); + btnNextPlate.Enabled = !activeForm.IsLastPlate(); + + mnuNestFirstPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsFirstPlate(); + btnFirstPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsFirstPlate(); + + mnuNestLastPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsLastPlate(); + btnLastPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsLastPlate(); + } + } + + private void UpdateLocationStatus() + { + if (activeForm == null) + { + locationStatusLabel.Text = string.Empty; + return; + } + + locationStatusLabel.Text = string.Format("Location: [{0}, {1}]", + activeForm.PlateView.CurrentPoint.X.ToString("n4"), + activeForm.PlateView.CurrentPoint.Y.ToString("n4")); + } + + private void UpdatePlateStatus() + { + if (activeForm == null) + { + plateIndexStatusLabel.Text = string.Empty; + plateSizeStatusLabel.Text = string.Empty; + plateQtyStatusLabel.Text = string.Empty; + return; + } + + plateIndexStatusLabel.Text = string.Format( + "Plate: {0} of {1}", + activeForm.CurrentPlateIndex + 1, + activeForm.PlateCount); + + plateSizeStatusLabel.Text = string.Format( + "Size: {0}", + activeForm.PlateView.Plate.Size); + + plateQtyStatusLabel.Text = string.Format( + "Qty: {0}", + activeForm.PlateView.Plate.Quantity); + } + + private void UpdateStatus() + { + UpdateLocationStatus(); + UpdatePlateStatus(); + } + + private void UpdateLocationMode() + { + if (activeForm == null) + return; + + if (clickUpdateLocation) + { + clickUpdateLocation = true; + locationStatusLabel.ForeColor = Color.Gray; + activeForm.PlateView.MouseMove -= PlateView_MouseMove; + activeForm.PlateView.MouseClick += PlateView_MouseClick; + } + else + { + clickUpdateLocation = false; + locationStatusLabel.ForeColor = Color.Black; + activeForm.PlateView.MouseMove += PlateView_MouseMove; + activeForm.PlateView.MouseClick -= PlateView_MouseClick; + } + } + + private void LoadPosts() + { + var exepath = Assembly.GetEntryAssembly().Location; + var postprocessordir = Path.Combine(Path.GetDirectoryName(exepath), "Posts"); + + if (Directory.Exists(postprocessordir)) + { + foreach (var file in Directory.GetFiles(postprocessordir, "*.dll")) + { + var types = Assembly.LoadFile(file).GetTypes(); + + foreach (var type in types) + { + if (type.GetInterfaces().Contains(typeof(IPostProcessor)) == false) + continue; + + var postProcessor = Activator.CreateInstance(type) as IPostProcessor; + var postProcessorMenuItem = new ToolStripMenuItem(postProcessor.Name); + postProcessorMenuItem.Tag = postProcessor; + postProcessorMenuItem.Click += PostProcessor_Click; + mnuNestPost.DropDownItems.Add(postProcessorMenuItem); + } + } + } + + mnuNestPost.Visible = mnuNestPost.DropDownItems.Count > 0; + } + + #region Overrides + + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + this.Refresh(); + } + + protected override void OnMdiChildActivate(EventArgs e) + { + base.OnMdiChildActivate(e); + + if (activeForm != null) + { + activeForm.PlateView.MouseMove -= PlateView_MouseMove; + activeForm.PlateView.MouseClick -= PlateView_MouseClick; + activeForm.PlateView.StatusChanged -= PlateView_StatusChanged; + } + + activeForm = ActiveMdiChild as EditNestForm; + + EnableCheck(); + UpdatePlateStatus(); + UpdateLocationStatus(); + + if (activeForm == null) + { + statusLabel1.Text = ""; + return; + } + + UpdateLocationMode(); + activeForm.PlateView.StatusChanged += PlateView_StatusChanged; + mnuViewDrawRapids.Checked = activeForm.PlateView.DrawRapid; + mnuViewDrawBounds.Checked = activeForm.PlateView.DrawBounds; + statusLabel1.Text = activeForm.PlateView.Status; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if (Settings.Default.CreateNewNestOnOpen) + New_Click(this, new EventArgs()); + } + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + base.OnClosing(e); + SaveSettings(); + } + + #endregion + + #region File Menu Events + + private void New_Click(object sender, EventArgs e) + { + var windowState = ActiveMdiChild != null + ? ActiveMdiChild.WindowState + : FormWindowState.Maximized; + + Nest nest; + + if (File.Exists(Properties.Settings.Default.NestTemplatePath)) + { + var reader = new NestReader(Properties.Settings.Default.NestTemplatePath); + nest = reader.Read(); + } + else + { + nest = new Nest(); + nest.Units = Properties.Settings.Default.DefaultUnit; + nest.PlateDefaults.EdgeSpacing = new Spacing(0, 0, 0, 0); + nest.PlateDefaults.PartSpacing = 0; + nest.PlateDefaults.Size = new OpenNest.Size(100, 100); + nest.PlateDefaults.Quadrant = 1; + } + + nest.DateCreated = DateTime.Now; + nest.DateLastModified = DateTime.Now; + + if (DateTime.Now.Date != Settings.Default.LastNestCreatedDate.Date) + { + Settings.Default.NestNumber = 1; + Settings.Default.LastNestCreatedDate = DateTime.Now.Date; + } + + nest.Name = GetNestName(DateTime.Now, Settings.Default.NestNumber++); + LoadNest(nest, windowState); + } + + private void Open_Click(object sender, EventArgs e) + { + var dlg = new OpenFileDialog(); + dlg.Filter = "Nest Files (*.zip) | *.zip"; + dlg.Multiselect = true; + + if (dlg.ShowDialog() == DialogResult.OK) + { + var reader = new NestReader(dlg.FileName); + var nest = reader.Read(); + LoadNest(nest); + } + } + + private void Save_Click(object sender, EventArgs e) + { + if (activeForm != null) + activeForm.Save(); + } + + private void SaveAs_Click(object sender, EventArgs e) + { + if (activeForm != null) + activeForm.SaveAs(); + } + + private void Export_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.Export(); + } + + private void ExportAll_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.ExportAll(); + } + + private void Exit_Click(object sender, EventArgs e) + { + Close(); + } + + #endregion File Menu Events + + #region Edit Menu Events + + private void EditCut_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + + var selectedParts = activeForm.PlateView.SelectedParts; + + if (selectedParts.Count > 0) + { + var partsToClone = selectedParts.Select(p => p.BasePart).ToList(); + activeForm.PlateView.SetAction(typeof(ActionClone), partsToClone); + + foreach (var part in partsToClone) + activeForm.PlateView.Plate.Parts.Remove(part); + } + } + + private void EditPaste_Click(object sender, EventArgs e) + { + } + + private void EditCopy_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + + var selectedParts = activeForm.PlateView.SelectedParts; + + if (selectedParts.Count > 0) + { + var partsToClone = selectedParts.Select(p => p.BasePart).ToList(); + activeForm.PlateView.SetAction(typeof(ActionClone), partsToClone); + } + } + + private void EditSelectAll_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.SelectAllParts(); + } + + #endregion Edit Menu Events + + #region View Menu Events + + private void ToggleDrawRapids_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.ToggleRapid(); + mnuViewDrawRapids.Checked = activeForm.PlateView.DrawRapid; + } + + private void ToggleDrawBounds_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.ToggleDrawBounds(); + mnuViewDrawBounds.Checked = activeForm.PlateView.DrawBounds; + } + + private void ZoomToArea_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.SetAction(typeof(ActionZoomWindow)); + } + + private void ZoomToFit_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.ZoomToFit(); + } + + private void ZoomToPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.ZoomToPlate(); + } + + private void ZoomToSelected_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.ZoomToSelected(); + } + + private void ZoomIn_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + + var pt = new Point( + activeForm.PlateView.Width / 2, + activeForm.PlateView.Height / 2); + + activeForm.PlateView.ZoomToControlPoint(pt, ZoomInFactor); + } + + private void ZoomOut_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + + var pt = new Point( + activeForm.PlateView.Width / 2, + activeForm.PlateView.Height / 2); + + activeForm.PlateView.ZoomToControlPoint(pt, ZoomOutFactor); + } + + #endregion View Menu Events + + #region Tools Menu Events + + private void MeasureArea_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + activeForm.PlateView.SetAction(typeof(ActionSelectArea)); + } + + private void SetOffsetIncrement_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + var form = new SetValueForm(); + form.Text = "Set Offset Increment"; + form.Value = activeForm.PlateView.OffsetIncrementDistance; + + if (form.ShowDialog() == DialogResult.OK) + activeForm.PlateView.OffsetIncrementDistance = form.Value; + } + + private void SetRotationIncrement_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + var form = new SetValueForm(); + form.Text = "Set Rotation Increment"; + form.Value = activeForm.PlateView.RotateIncrementAngle; + form.Maximum = 360; + + if (form.ShowDialog() == DialogResult.OK) + activeForm.PlateView.RotateIncrementAngle = form.Value; + } + + private void Options_Click(object sender, EventArgs e) + { + var form = new OptionsForm(); + form.ShowDialog(); + } + + private void AlignLeft_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Left); + } + + private void AlignRight_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Right); + } + + private void AlignTop_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Top); + } + + private void AlignBottom_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Bottom); + } + + private void AlignVertical_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Vertically); + } + + private void AlignHorizontal_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.Horizontally); + } + + private void EvenlySpaceHorizontally_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.EvenlySpaceHorizontally); + } + + private void EvenlySpaceVertically_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.PlateView.AlignSelected(AlignType.EvenlySpaceVertically); + } + + #endregion Tools Menu Events + + #region Nest Menu Events + + private void Import_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.Import(); + } + + private void EditNest_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.ShowNestInfoEditor(); + } + + private void RemoveEmptyPlates_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.Nest.Plates.RemoveEmptyPlates(); + } + + private void LoadFirstPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.LoadFirstPlate(); + } + + private void LoadLastPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.LoadLastPlate(); + } + + private void LoadPreviousPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.LoadPreviousPlate(); + } + + private void LoadNextPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.LoadNextPlate(); + } + + private void RunAutoNest_Click(object sender, EventArgs e) + { + var form = new AutoNestForm(activeForm.Nest); + form.AllowPlateCreation = true; + + if (form.ShowDialog() != System.Windows.Forms.DialogResult.OK) + return; + + var items = form.GetNestItems(); + var qty = new int[items.Count]; + + while (true) + { + for (int i = 0; i < items.Count; i++) + qty[i] = items[i].Drawing.Quantity.Remaining; + + var plate = activeForm.PlateView.Plate.Parts.Count > 0 + ? activeForm.Nest.CreatePlate() + : activeForm.PlateView.Plate; + + var engine = new NestEngine(plate); + + if (!engine.Pack(items)) + break; + + activeForm.Nest.UpdateDrawingQuantities(); + + for (int i = 0; i < items.Count; i++) + items[i].Quantity -= qty[i] - items[i].Drawing.Quantity.Remaining; + } + } + + private void SequenceAllPlates_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + activeForm.AutoSequenceAllPlates(); + } + + private void PostProcessor_Click(object sender, EventArgs e) + { + var menuItem = (ToolStripMenuItem)sender; + var postProcessor = menuItem.Tag as IPostProcessor; + + if (postProcessor == null) + return; + + var dialog = new SaveFileDialog(); + dialog.Filter = "CNC File (*.cnc) | *.cnc"; + dialog.FileName = activeForm.Nest.Name; + + if (dialog.ShowDialog() == DialogResult.OK) + { + var path = dialog.FileName; + postProcessor.Post(activeForm.Nest, path); + } + } + + private void CalculateNestCutTime_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + activeForm.CalculateNestCutTime(); + } + + #endregion Nest Menu Events + + #region Plate Menu Events + + private void SetAsNestDefault_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.SetCurrentPlateAsNestDefault(); + } + + private void FillPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + if (activeForm.Nest.Drawings.Count == 0) + return; + + var form = new FillPlateForm(activeForm.Nest.Drawings); + form.ShowDialog(); + + var drawing = form.SelectedDrawing; + + if (drawing == null) + return; + + var engine = new NestEngine(activeForm.PlateView.Plate); + engine.Fill(new NestItem + { + Drawing = drawing + }); + + activeForm.PlateView.Invalidate(); + } + + private void FillArea_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + if (activeForm.Nest.Drawings.Count == 0) + return; + + var form = new FillPlateForm(activeForm.Nest.Drawings); + form.ShowDialog(); + + var drawing = form.SelectedDrawing; + + if (drawing == null) + return; + + activeForm.PlateView.SetAction(typeof(ActionFillArea), drawing); + } + + private void AddPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.Nest.CreatePlate(); + NavigationEnableCheck(); + } + + private void EditPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.EditPlate(); + } + + private void RemovePlate_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.RemoveCurrentPlate(); + } + + private void ResizeToFitParts_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.ResizePlateToFitParts(); + UpdatePlateStatus(); + } + + private void RotateCw_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.RotateCw(); + } + + private void RotateCcw_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.RotateCcw(); + } + + private void Rotate180_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.Rotate180(); + } + + private void OpenInExternalCad_Click(object sender, EventArgs e) + { + if (activeForm == null) return; + activeForm.OpenCurrentPlate(); + } + + private void CalculatePlateCutTime_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + activeForm.CalculateCurrentPlateCutTime(); + } + + private void AutoSequenceCurrentPlate_Click(object sender, EventArgs e) + { + if (activeForm == null) + return; + + activeForm.AutoSequenceCurrentPlate(); + } + + private void ManualSequenceParts_Click(object sender, EventArgs e) + { + if (activeForm == null || activeForm.PlateView.Plate.Parts.Count < 2) + return; + + activeForm.PlateView.SetAction(typeof(ActionSetSequence)); + } + + #endregion Plate Menu Events + + #region Window Menu Events + + private void TileVertical_Click(object sender, EventArgs e) + { + LayoutMdi(MdiLayout.TileVertical); + } + + private void TileHorizontal_Click(object sender, EventArgs e) + { + LayoutMdi(MdiLayout.TileHorizontal); + } + + private void CascadeWindows_Click(object sender, EventArgs e) + { + LayoutMdi(MdiLayout.Cascade); + } + + private void Close_Click(object sender, EventArgs e) + { + if (ActiveMdiChild != null) + ActiveMdiChild.Close(); + } + + private void CloseAll_Click(object sender, EventArgs e) + { + foreach (var mdiChild in MdiChildren) + mdiChild.Close(); + } + + #endregion Window Menu Events + + #region Statusbar Events + + private void LocationStatusLabel_Click(object sender, EventArgs e) + { + clickUpdateLocation = !clickUpdateLocation; + UpdateLocationMode(); + } + + #endregion Statusbar Events + + #region PlateView Events + + private void PlateView_MouseMove(object sender, MouseEventArgs e) + { + UpdateLocationStatus(); + } + + private void PlateView_MouseClick(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + UpdateLocationStatus(); + } + + private void PlateView_StatusChanged(object sender, EventArgs e) + { + statusLabel1.Text = activeForm.PlateView.Status; + } + + #endregion PlateView Events + + private void centerPartsToolStripMenuItem_Click(object sender, EventArgs e) + { + var plateCenter = this.activeForm.PlateView.Plate.BoundingBox(false).Center; + var partsCenter = this.activeForm.PlateView.Parts.GetBoundingBox().Center; + + var offset = plateCenter - partsCenter; + + foreach (var part in this.activeForm.PlateView.Parts) + { + part.Offset(offset); + } + + activeForm.PlateView.Invalidate(); + } + } +} diff --git a/Source/OpenNest/Forms/MainForm.resx b/Source/OpenNest/Forms/MainForm.resx new file mode 100644 index 0000000..24c4237 --- /dev/null +++ b/Source/OpenNest/Forms/MainForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 126, 17 + + + 236, 17 + + + 59 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/OptionsForm.Designer.cs b/Source/OpenNest/Forms/OptionsForm.Designer.cs new file mode 100644 index 0000000..c1d4776 --- /dev/null +++ b/Source/OpenNest/Forms/OptionsForm.Designer.cs @@ -0,0 +1,256 @@ +namespace OpenNest.Forms +{ + partial class OptionsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.numericUpDown1 = new OpenNest.Controls.NumericUpDown(); + this.label2 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.numericUpDown2 = new OpenNest.Controls.NumericUpDown(); + this.button1 = new System.Windows.Forms.Button(); + this.saveButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // checkBox1 + // + this.checkBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.checkBox1.AutoSize = true; + this.tableLayoutPanel1.SetColumnSpan(this.checkBox1, 2); + this.checkBox1.Location = new System.Drawing.Point(3, 130); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(281, 20); + this.checkBox1.TabIndex = 7; + this.checkBox1.Text = "Create a new nest on startup"; + this.checkBox1.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 52); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(145, 16); + this.label1.TabIndex = 3; + this.label1.Text = "Auto-size plate factor:"; + // + // numericUpDown1 + // + this.numericUpDown1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown1.DecimalPlaces = 4; + this.numericUpDown1.Increment = new decimal(new int[] { + 125, + 0, + 0, + 196608}); + this.numericUpDown1.Location = new System.Drawing.Point(154, 49); + this.numericUpDown1.Name = "numericUpDown1"; + this.numericUpDown1.Size = new System.Drawing.Size(130, 22); + this.numericUpDown1.Suffix = ""; + this.numericUpDown1.TabIndex = 4; + this.toolTip1.SetToolTip(this.numericUpDown1, "The amount to round the plate size up."); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 92); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(145, 16); + this.label2.TabIndex = 5; + this.label2.Text = "Import spline precision:\r\n"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 4; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 297F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F)); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.checkBox1, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown2, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown1, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.button1, 3, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(684, 160); + this.tableLayoutPanel1.TabIndex = 0; + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.SetColumnSpan(this.textBox1, 2); + this.textBox1.Location = new System.Drawing.Point(154, 9); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(427, 22); + this.textBox1.TabIndex = 1; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(145, 16); + this.label3.TabIndex = 0; + this.label3.Text = "Nest Template Path:"; + // + // numericUpDown2 + // + this.numericUpDown2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown2.Location = new System.Drawing.Point(154, 89); + this.numericUpDown2.Maximum = new decimal(new int[] { + 360, + 0, + 0, + 0}); + this.numericUpDown2.Minimum = new decimal(new int[] { + 3, + 0, + 0, + 0}); + this.numericUpDown2.Name = "numericUpDown2"; + this.numericUpDown2.Size = new System.Drawing.Size(130, 22); + this.numericUpDown2.Suffix = ""; + this.numericUpDown2.TabIndex = 6; + this.numericUpDown2.Value = new decimal(new int[] { + 200, + 0, + 0, + 0}); + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.button1.Location = new System.Drawing.Point(588, 6); + this.button1.Margin = new System.Windows.Forms.Padding(4); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(92, 28); + this.button1.TabIndex = 2; + this.button1.Text = "Browse..."; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.BrowseNestTemplatePath_Click); + // + // saveButton + // + this.saveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.saveButton.Location = new System.Drawing.Point(507, 11); + this.saveButton.Margin = new System.Windows.Forms.Padding(4); + this.saveButton.Name = "saveButton"; + this.saveButton.Size = new System.Drawing.Size(90, 28); + this.saveButton.TabIndex = 0; + this.saveButton.Text = "Save"; + this.saveButton.UseVisualStyleBackColor = true; + this.saveButton.Click += new System.EventHandler(this.SaveSettings_Click); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(605, 11); + this.cancelButton.Margin = new System.Windows.Forms.Padding(4); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(90, 28); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.cancelButton); + this.bottomPanel1.Controls.Add(this.saveButton); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 368); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(708, 50); + this.bottomPanel1.TabIndex = 1; + // + // OptionsForm + // + this.AcceptButton = this.saveButton; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(708, 418); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "OptionsForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Options"; + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit(); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button saveButton; + private System.Windows.Forms.Label label1; + private Controls.NumericUpDown numericUpDown1; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Label label2; + private Controls.NumericUpDown numericUpDown2; + private Controls.BottomPanel bottomPanel1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/OptionsForm.cs b/Source/OpenNest/Forms/OptionsForm.cs new file mode 100644 index 0000000..48896f7 --- /dev/null +++ b/Source/OpenNest/Forms/OptionsForm.cs @@ -0,0 +1,50 @@ +using System.Windows.Forms; +using OpenNest.Properties; + +namespace OpenNest.Forms +{ + public partial class OptionsForm : Form + { + public OptionsForm() + { + InitializeComponent(); + } + + protected override void OnLoad(System.EventArgs e) + { + base.OnLoad(e); + LoadSettings(); + } + + private void LoadSettings() + { + textBox1.Text = Settings.Default.NestTemplatePath; + checkBox1.Checked = Settings.Default.CreateNewNestOnOpen; + numericUpDown1.Value = (decimal)Settings.Default.AutoSizePlateFactor; + numericUpDown2.Value = (decimal)Settings.Default.ImportSplinePrecision; + } + + private void SaveSettings() + { + Settings.Default.NestTemplatePath = textBox1.Text; + Settings.Default.CreateNewNestOnOpen = checkBox1.Checked; + Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value; + Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value; + Settings.Default.Save(); + } + + private void SaveSettings_Click(object sender, System.EventArgs e) + { + SaveSettings(); + } + + private void BrowseNestTemplatePath_Click(object sender, System.EventArgs e) + { + var dlg = new OpenFileDialog(); + dlg.Filter = "Template File|*.nstdot"; + + if (dlg.ShowDialog() == DialogResult.OK) + textBox1.Text = dlg.FileName; + } + } +} diff --git a/Source/OpenNest/Forms/OptionsForm.resx b/Source/OpenNest/Forms/OptionsForm.resx new file mode 100644 index 0000000..df8339b --- /dev/null +++ b/Source/OpenNest/Forms/OptionsForm.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/SequenceForm.Designer.cs b/Source/OpenNest/Forms/SequenceForm.Designer.cs new file mode 100644 index 0000000..e3d2d39 --- /dev/null +++ b/Source/OpenNest/Forms/SequenceForm.Designer.cs @@ -0,0 +1,111 @@ +namespace OpenNest.Forms +{ + partial class SequenceForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.numericUpDown1 = new OpenNest.Controls.NumericUpDown(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.numericUpDown1, 1, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(220, 36); + this.tableLayoutPanel1.TabIndex = 0; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 11); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(62, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Sequence :"; + // + // numericUpDown1 + // + this.numericUpDown1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDown1.Location = new System.Drawing.Point(71, 8); + this.numericUpDown1.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numericUpDown1.Name = "numericUpDown1"; + this.numericUpDown1.Size = new System.Drawing.Size(146, 20); + this.numericUpDown1.TabIndex = 1; + this.numericUpDown1.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numericUpDown1.Leave += new System.EventHandler(this.numericUpDown1_Leave); + // + // SequenceForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(220, 36); + this.ControlBox = false; + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Location = new System.Drawing.Point(100, 100); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SequenceForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "Set Sequence"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + public Controls.NumericUpDown numericUpDown1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/SequenceForm.cs b/Source/OpenNest/Forms/SequenceForm.cs new file mode 100644 index 0000000..d800475 --- /dev/null +++ b/Source/OpenNest/Forms/SequenceForm.cs @@ -0,0 +1,18 @@ +using System; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class SequenceForm : Form + { + public SequenceForm() + { + InitializeComponent(); + } + + private void numericUpDown1_Leave(object sender, EventArgs e) + { + numericUpDown1.Validate(); + } + } +} diff --git a/Source/OpenNest/Forms/SequenceForm.resx b/Source/OpenNest/Forms/SequenceForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/SequenceForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/SetValueForm.Designer.cs b/Source/OpenNest/Forms/SetValueForm.Designer.cs new file mode 100644 index 0000000..82d49ac --- /dev/null +++ b/Source/OpenNest/Forms/SetValueForm.Designer.cs @@ -0,0 +1,122 @@ +namespace OpenNest.Forms +{ + partial class SetValueForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonSet = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.labelIncrement = new System.Windows.Forms.Label(); + this.numericUpDownValue = new OpenNest.Controls.NumericUpDown(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownValue)).BeginInit(); + this.SuspendLayout(); + // + // buttonSet + // + this.buttonSet.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.buttonSet.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonSet.Location = new System.Drawing.Point(256, 6); + this.buttonSet.Name = "buttonSet"; + this.buttonSet.Size = new System.Drawing.Size(85, 28); + this.buttonSet.TabIndex = 2; + this.buttonSet.Text = "Set"; + this.buttonSet.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.buttonSet, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.labelIncrement, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.numericUpDownValue, 1, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 5); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(344, 41); + this.tableLayoutPanel1.TabIndex = 0; + // + // labelIncrement + // + this.labelIncrement.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.labelIncrement.AutoSize = true; + this.labelIncrement.Location = new System.Drawing.Point(3, 12); + this.labelIncrement.Name = "labelIncrement"; + this.labelIncrement.Size = new System.Drawing.Size(72, 16); + this.labelIncrement.TabIndex = 0; + this.labelIncrement.Text = "Increment :"; + this.labelIncrement.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numericUpDownValue + // + this.numericUpDownValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.numericUpDownValue.DecimalPlaces = 4; + this.numericUpDownValue.Location = new System.Drawing.Point(80, 9); + this.numericUpDownValue.Margin = new System.Windows.Forms.Padding(2); + this.numericUpDownValue.Maximum = new decimal(new int[] { + 999999, + 0, + 0, + 0}); + this.numericUpDownValue.Name = "numericUpDownValue"; + this.numericUpDownValue.Size = new System.Drawing.Size(171, 22); + this.numericUpDownValue.TabIndex = 1; + // + // SetValueForm + // + this.AcceptButton = this.buttonSet; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(356, 51); + this.Controls.Add(this.tableLayoutPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SetValueForm"; + this.Padding = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownValue)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button buttonSet; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label labelIncrement; + private Controls.NumericUpDown numericUpDownValue; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/SetValueForm.cs b/Source/OpenNest/Forms/SetValueForm.cs new file mode 100644 index 0000000..8a9be2c --- /dev/null +++ b/Source/OpenNest/Forms/SetValueForm.cs @@ -0,0 +1,48 @@ +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class SetValueForm : Form + { + public SetValueForm() + { + InitializeComponent(); + } + + protected override bool ProcessDialogKey(Keys keyData) + { + switch (keyData) + { + case Keys.Escape: + Close(); + break; + } + + return base.ProcessDialogKey(keyData); + } + + public double Minimum + { + get { return (double)numericUpDownValue.Minimum; } + set { numericUpDownValue.Minimum = (decimal)value; } + } + + public double Maximum + { + get { return (double)numericUpDownValue.Maximum; } + set { numericUpDownValue.Maximum = (decimal)value; } + } + + public double Increment + { + get { return (double)numericUpDownValue.Increment; } + set { numericUpDownValue.Increment = (decimal)value; } + } + + public double Value + { + get { return (double)numericUpDownValue.Value; } + set { numericUpDownValue.Value = (decimal)value; } + } + } +} diff --git a/Source/OpenNest/Forms/SetValueForm.resx b/Source/OpenNest/Forms/SetValueForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/SetValueForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/Forms/TimingForm.Designer.cs b/Source/OpenNest/Forms/TimingForm.Designer.cs new file mode 100644 index 0000000..a48c44c --- /dev/null +++ b/Source/OpenNest/Forms/TimingForm.Designer.cs @@ -0,0 +1,360 @@ +namespace OpenNest.Forms +{ + partial class TimingForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.horizontalLine2 = new OpenNest.Controls.HorizontalLine(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.cutTimeLabel = new System.Windows.Forms.Label(); + this.cutDistanceLabel = new System.Windows.Forms.Label(); + this.rapidDistanceLabel = new System.Windows.Forms.Label(); + this.interectionsLabel = new System.Windows.Forms.Label(); + this.piercesLabel = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.feedrateLabel = new System.Windows.Forms.Label(); + this.rapidLabel = new System.Windows.Forms.Label(); + this.pierceTimeLabel = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.bottomPanel1 = new OpenNest.Controls.BottomPanel(); + this.tableLayoutPanel1.SuspendLayout(); + this.bottomPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.horizontalLine2, 0, 5); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.label5, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.cutTimeLabel, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.cutDistanceLabel, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.rapidDistanceLabel, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.interectionsLabel, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.piercesLabel, 1, 4); + this.tableLayoutPanel1.Controls.Add(this.label6, 0, 6); + this.tableLayoutPanel1.Controls.Add(this.label7, 0, 7); + this.tableLayoutPanel1.Controls.Add(this.label8, 0, 8); + this.tableLayoutPanel1.Controls.Add(this.feedrateLabel, 1, 6); + this.tableLayoutPanel1.Controls.Add(this.rapidLabel, 1, 7); + this.tableLayoutPanel1.Controls.Add(this.pierceTimeLabel, 1, 8); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(5); + this.tableLayoutPanel1.RowCount = 10; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 11.11111F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(340, 319); + this.tableLayoutPanel1.TabIndex = 1; + // + // horizontalLine2 + // + this.horizontalLine2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel1.SetColumnSpan(this.horizontalLine2, 2); + this.horizontalLine2.Location = new System.Drawing.Point(9, 176); + this.horizontalLine2.Margin = new System.Windows.Forms.Padding(4); + this.horizontalLine2.Name = "horizontalLine2"; + this.horizontalLine2.Size = new System.Drawing.Size(322, 10); + this.horizontalLine2.TabIndex = 3; + this.horizontalLine2.Text = "horizontalLine1"; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 13); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(113, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Cut Time :"; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 45); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(113, 16); + this.label2.TabIndex = 0; + this.label2.Text = "Cut Distance :\r\n"; + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(9, 77); + this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(113, 16); + this.label4.TabIndex = 0; + this.label4.Text = "Rapid Distance :"; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(9, 141); + this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(113, 16); + this.label3.TabIndex = 0; + this.label3.Text = "# of Pierces :"; + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(9, 109); + this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(113, 16); + this.label5.TabIndex = 0; + this.label5.Text = "# of Intersections :"; + // + // cutTimeLabel + // + this.cutTimeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.cutTimeLabel.AutoSize = true; + this.cutTimeLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cutTimeLabel.Location = new System.Drawing.Point(130, 13); + this.cutTimeLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.cutTimeLabel.Name = "cutTimeLabel"; + this.cutTimeLabel.Size = new System.Drawing.Size(201, 16); + this.cutTimeLabel.TabIndex = 0; + this.cutTimeLabel.Text = "-----"; + // + // cutDistanceLabel + // + this.cutDistanceLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.cutDistanceLabel.AutoSize = true; + this.cutDistanceLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cutDistanceLabel.Location = new System.Drawing.Point(130, 45); + this.cutDistanceLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.cutDistanceLabel.Name = "cutDistanceLabel"; + this.cutDistanceLabel.Size = new System.Drawing.Size(201, 16); + this.cutDistanceLabel.TabIndex = 0; + this.cutDistanceLabel.Text = "-----"; + // + // rapidDistanceLabel + // + this.rapidDistanceLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.rapidDistanceLabel.AutoSize = true; + this.rapidDistanceLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rapidDistanceLabel.Location = new System.Drawing.Point(130, 77); + this.rapidDistanceLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.rapidDistanceLabel.Name = "rapidDistanceLabel"; + this.rapidDistanceLabel.Size = new System.Drawing.Size(201, 16); + this.rapidDistanceLabel.TabIndex = 0; + this.rapidDistanceLabel.Text = "-----"; + // + // interectionsLabel + // + this.interectionsLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.interectionsLabel.AutoSize = true; + this.interectionsLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.interectionsLabel.Location = new System.Drawing.Point(130, 109); + this.interectionsLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.interectionsLabel.Name = "interectionsLabel"; + this.interectionsLabel.Size = new System.Drawing.Size(201, 16); + this.interectionsLabel.TabIndex = 0; + this.interectionsLabel.Text = "-----"; + // + // piercesLabel + // + this.piercesLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.piercesLabel.AutoSize = true; + this.piercesLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.piercesLabel.Location = new System.Drawing.Point(130, 141); + this.piercesLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.piercesLabel.Name = "piercesLabel"; + this.piercesLabel.Size = new System.Drawing.Size(201, 16); + this.piercesLabel.TabIndex = 0; + this.piercesLabel.Text = "-----"; + // + // label6 + // + this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(9, 205); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(113, 16); + this.label6.TabIndex = 0; + this.label6.Text = "Feedrate :"; + // + // label7 + // + this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(9, 237); + this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(113, 16); + this.label7.TabIndex = 0; + this.label7.Text = "Rapid Feedrate :"; + // + // label8 + // + this.label8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(9, 269); + this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(113, 16); + this.label8.TabIndex = 0; + this.label8.Text = "Pierce Time :"; + // + // feedrateLabel + // + this.feedrateLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.feedrateLabel.AutoSize = true; + this.feedrateLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.feedrateLabel.Location = new System.Drawing.Point(130, 205); + this.feedrateLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.feedrateLabel.Name = "feedrateLabel"; + this.feedrateLabel.Size = new System.Drawing.Size(201, 16); + this.feedrateLabel.TabIndex = 0; + this.feedrateLabel.Text = "-----"; + // + // rapidLabel + // + this.rapidLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.rapidLabel.AutoSize = true; + this.rapidLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rapidLabel.Location = new System.Drawing.Point(130, 237); + this.rapidLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.rapidLabel.Name = "rapidLabel"; + this.rapidLabel.Size = new System.Drawing.Size(201, 16); + this.rapidLabel.TabIndex = 0; + this.rapidLabel.Text = "-----"; + // + // pierceTimeLabel + // + this.pierceTimeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.pierceTimeLabel.AutoSize = true; + this.pierceTimeLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.pierceTimeLabel.Location = new System.Drawing.Point(130, 269); + this.pierceTimeLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.pierceTimeLabel.Name = "pierceTimeLabel"; + this.pierceTimeLabel.Size = new System.Drawing.Size(201, 16); + this.pierceTimeLabel.TabIndex = 0; + this.pierceTimeLabel.Text = "-----"; + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; + this.button1.Location = new System.Drawing.Point(237, 11); + this.button1.Margin = new System.Windows.Forms.Padding(4); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(90, 28); + this.button1.TabIndex = 4; + this.button1.Text = "Close"; + this.button1.UseVisualStyleBackColor = true; + // + // bottomPanel1 + // + this.bottomPanel1.Controls.Add(this.button1); + this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomPanel1.Location = new System.Drawing.Point(0, 319); + this.bottomPanel1.Name = "bottomPanel1"; + this.bottomPanel1.Size = new System.Drawing.Size(340, 50); + this.bottomPanel1.TabIndex = 5; + // + // TimingForm + // + this.AcceptButton = this.button1; + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.button1; + this.ClientSize = new System.Drawing.Size(340, 369); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.bottomPanel1); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TimingForm"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Timing Information"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.bottomPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label cutTimeLabel; + private System.Windows.Forms.Label cutDistanceLabel; + private System.Windows.Forms.Label rapidDistanceLabel; + private System.Windows.Forms.Label interectionsLabel; + private System.Windows.Forms.Label piercesLabel; + private Controls.HorizontalLine horizontalLine2; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label feedrateLabel; + private System.Windows.Forms.Label rapidLabel; + private System.Windows.Forms.Label pierceTimeLabel; + private Controls.BottomPanel bottomPanel1; + } +} \ No newline at end of file diff --git a/Source/OpenNest/Forms/TimingForm.cs b/Source/OpenNest/Forms/TimingForm.cs new file mode 100644 index 0000000..67579e2 --- /dev/null +++ b/Source/OpenNest/Forms/TimingForm.cs @@ -0,0 +1,72 @@ +using System; +using System.Windows.Forms; + +namespace OpenNest.Forms +{ + public partial class TimingForm : Form + { + public TimingForm() + { + InitializeComponent(); + } + + public Units Units { get; set; } + + public void SetCutTime(TimeSpan timeSpan) + { + cutTimeLabel.Text = GetTimeMsg(timeSpan); + } + + public void SetCutDistance(double dist) + { + cutDistanceLabel.Text = string.Format("{0} {1}", Math.Round(dist, 4), UnitsHelper.GetShortString(Units)); + } + + public void SetRapidDistance(double dist) + { + rapidDistanceLabel.Text = string.Format("{0} {1}", Math.Round(dist, 4), UnitsHelper.GetShortString(Units)); + } + + public void SetIntersectionCount(int count) + { + interectionsLabel.Text = count.ToString(); + } + + public void SetPierceCount(int count) + { + piercesLabel.Text = count.ToString(); + } + + public void SetCutParameters(CutParameters cutparams) + { + feedrateLabel.Text = string.Format("{0} {1}/{2}", cutparams.Feedrate, UnitsHelper.GetShortString(Units), UnitsHelper.GetShortTimeUnit(Units)); + rapidLabel.Text = string.Format("{0} {1}/{2}", cutparams.RapidTravelRate, UnitsHelper.GetShortString(Units), UnitsHelper.GetShortTimeUnit(Units)); + pierceTimeLabel.Text = GetTimeMsg(cutparams.PierceTime); + } + + public static string GetTimeMsg(TimeSpan time) + { + int hrs = time.Days * 24 + time.Hours; + int min = time.Minutes; + int sec = time.Seconds; + + string msg = string.Empty; + + if (hrs > 0) + { + msg += hrs.ToString() + "h "; + msg += min.ToString().PadLeft(2, '0') + "m "; + msg += sec.ToString().PadLeft(2, '0') + "s"; + } + else if (min > 0) + { + msg += min.ToString().PadLeft(2, '0') + "m "; + msg += sec.ToString().PadLeft(2, '0') + "s"; + } + else + msg += time.TotalSeconds.ToString("n0").PadLeft(2, '0') + "s"; + + return msg; + } + } +} diff --git a/Source/OpenNest/Forms/TimingForm.resx b/Source/OpenNest/Forms/TimingForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/OpenNest/Forms/TimingForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/OpenNest/GraphicsHelper.cs b/Source/OpenNest/GraphicsHelper.cs new file mode 100644 index 0000000..ebb954f --- /dev/null +++ b/Source/OpenNest/GraphicsHelper.cs @@ -0,0 +1,307 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using OpenNest.CNC; +using OpenNest.Geometry; + +namespace OpenNest +{ + internal static class GraphicsHelper + { + public static GraphicsPath GetGraphicsPath(this Program pgm) + { + var path = new GraphicsPath(); + var curpos = Vector.Zero; + + AddProgram(path, pgm, pgm.Mode, ref curpos); + + return path; + } + + public static GraphicsPath GetGraphicsPath(this Program pgm, Vector origin) + { + var path = new GraphicsPath(); + var curpos = origin; + + AddProgram(path, pgm, pgm.Mode, ref curpos); + + return path; + } + + public static GraphicsPath GetGraphicsPath(this Shape shape) + { + var path = new GraphicsPath(); + + AddShape(path, shape); + + return path; + } + + public static Image GetImage(this Program pgm, System.Drawing.Size size) + { + return pgm.GetImage(size, Pens.Black, null); + } + + public static Image GetImage(this Program pgm, System.Drawing.Size size, Pen pen) + { + return pgm.GetImage(size, pen, null); + } + + public static Image GetImage(this Program pgm, System.Drawing.Size size, Pen pen, Brush brush) + { + var img = new Bitmap(size.Width, size.Height); + var path = pgm.GetGraphicsPath(); + var bounds = path.GetBounds(); + + var scalex = (size.Height - 10) / bounds.Height; + var scaley = (size.Width - 10) / bounds.Width; + var scale = scalex < scaley ? scalex : scaley; + + var matrix = new Matrix(); + matrix.Scale(scale, -scale); + + path.Transform(matrix); + + bounds = path.GetBounds(); + + var offset = new PointF( + (size.Width - bounds.Width) * 0.5f - bounds.X, + (size.Height - bounds.Height) * 0.5f - bounds.Y); + + var graphics = Graphics.FromImage(img); + graphics.TranslateTransform(offset.X, offset.Y); + + if (brush != null) + graphics.FillPath(brush, path); + + if (pen == null) + pen = Pens.Black; + + graphics.DrawPath(pen, path); + + matrix.Dispose(); + graphics.Dispose(); + + return img; + } + + private static void AddArc(GraphicsPath path, CircularMove arc, Mode mode, ref Vector curpos) + { + var endpt = arc.EndPoint; + var center = arc.CenterPoint; + + if (mode == Mode.Incremental) + { + endpt += curpos; + center += curpos; + } + + // start angle in degrees + var startAngle = Angle.ToDegrees(Math.Atan2( + curpos.Y - center.Y, + curpos.X - center.X)); + + // end angle in degrees + var endAngle = Angle.ToDegrees(Math.Atan2( + endpt.Y - center.Y, + endpt.X - center.X)); + + endAngle = Angle.NormalizeDeg(endAngle); + startAngle = Angle.NormalizeDeg(startAngle); + + if (arc.Rotation == RotationType.CCW && endAngle < startAngle) + endAngle += 360.0; + else if (arc.Rotation == RotationType.CW && startAngle < endAngle) + startAngle += 360.0; + + var dx = endpt.X - center.X; + var dy = endpt.Y - center.Y; + + var radius = Math.Sqrt(dx * dx + dy * dy); + + var pt = new PointF((float)(center.X - radius), (float)(center.Y - radius)); + var size = (float)(radius * 2.0); + + if (startAngle.IsEqualTo(endAngle)) + { + path.AddEllipse(pt.X, pt.Y, size, size); + } + else + { + var sweepAngle = (endAngle - startAngle); + + path.AddArc( + pt.X, pt.Y, + size, size, + (float)startAngle, + (float)sweepAngle); + + if (arc.Layer == LayerType.Leadin || arc.Layer == LayerType.Leadout) + { + path.AddArc(pt.X, pt.Y, size, size, (float)(-startAngle + sweepAngle), (float)-sweepAngle); + path.CloseFigure(); + } + } + + curpos = endpt; + } + + private static void AddLine(GraphicsPath path, LinearMove line, Mode mode, ref Vector curpos) + { + var pt = line.EndPoint; + + if (mode == Mode.Incremental) + pt += curpos; + + var pt1 = new PointF((float)curpos.X, (float)curpos.Y); + var pt2 = new PointF((float)pt.X, (float)pt.Y); + + path.AddLine(pt1, pt2); + + if (line.Layer == LayerType.Leadin || line.Layer == LayerType.Leadout) + path.CloseFigure(); + + curpos = pt; + } + + private static void AddLine(GraphicsPath path, RapidMove line, Mode mode, ref Vector curpos) + { + var pt = line.EndPoint; + + if (mode == Mode.Incremental) + pt += curpos; + + path.CloseFigure(); + + curpos = pt; + } + + private static void AddProgram(GraphicsPath path, Program pgm, Mode mode, ref Vector curpos) + { + mode = pgm.Mode; + + for (int i = 0; i < pgm.Length; ++i) + { + var code = pgm[i]; + + switch (code.Type) + { + case CodeType.CircularMove: + AddArc(path, (CircularMove)code, mode, ref curpos); + break; + + case CodeType.LinearMove: + AddLine(path, (LinearMove)code, mode, ref curpos); + break; + + case CodeType.RapidMove: + AddLine(path, (RapidMove)code, mode, ref curpos); + break; + + case CodeType.SubProgramCall: + { + var tmpmode = mode; + var subpgm = (SubProgramCall)code; + + if (subpgm.Program != null) + { + path.StartFigure(); + AddProgram(path, subpgm.Program, mode, ref curpos); + } + + mode = tmpmode; + break; + } + } + } + } + + private static void AddArc(GraphicsPath path, Arc arc) + { + var diameter = arc.Diameter; + + var endAngle = Angle.NormalizeDeg(Angle.ToDegrees(arc.EndAngle)); + var startAngle = Angle.NormalizeDeg(Angle.ToDegrees(arc.StartAngle)); + + if (arc.Rotation == RotationType.CCW && endAngle < startAngle) + endAngle += 360.0; + else if (arc.Rotation == RotationType.CW && startAngle < endAngle) + startAngle += 360.0; + + var sweepAngle = (endAngle - startAngle); + + path.AddArc( + (float)(arc.Center.X - arc.Radius), + (float)(arc.Center.Y - arc.Radius), + (float)diameter, + (float)diameter, + (float)(startAngle), + (float)sweepAngle); + } + + private static void AddCircle(GraphicsPath path, Circle circle) + { + var diameter = circle.Diameter; + + path.AddEllipse( + (float)(circle.Center.X - circle.Radius), + (float)(circle.Center.Y - circle.Radius), + (float)diameter, + (float)diameter); + } + + private static void AddLine(GraphicsPath path, Line line) + { + path.AddLine( + (float)line.StartPoint.X, + (float)line.StartPoint.Y, + (float)line.EndPoint.X, + (float)line.EndPoint.Y); + } + + private static void AddShape(GraphicsPath path, Shape shape) + { + foreach (var entity in shape.Entities) + { + switch (entity.Type) + { + case EntityType.Arc: + AddArc(path, (Arc)entity); + break; + + case EntityType.Circle: + AddCircle(path, (Circle)entity); + break; + + case EntityType.Line: + AddLine(path, (Line)entity); + break; + + case EntityType.Polygon: + AddPolygon(path, (Polygon)entity); + break; + + case EntityType.Shape: + var subpath = new GraphicsPath(); + AddShape(subpath, (Shape)entity); + path.AddPath(subpath, false); + subpath.Dispose(); + break; + } + } + } + + private static void AddPolygon(GraphicsPath path, Polygon polygon) + { + var pts = new PointF[polygon.Vertices.Count]; + + for (int i = 0; i < pts.Length; i++) + { + var pt = polygon.Vertices[i]; + pts[i] = new PointF((float)pt.X, (float)pt.Y); + } + + path.AddPolygon(pts); + } + } +} diff --git a/Source/OpenNest/IO/DxfExporter.cs b/Source/OpenNest/IO/DxfExporter.cs new file mode 100644 index 0000000..2e29c51 --- /dev/null +++ b/Source/OpenNest/IO/DxfExporter.cs @@ -0,0 +1,287 @@ +using System; +using System.Diagnostics; +using System.IO; +using netDxf; +using netDxf.Entities; +using netDxf.Tables; +using OpenNest.CNC; + +namespace OpenNest.IO +{ + using Layer = netDxf.Tables.Layer; + using Line = netDxf.Entities.Line; + + public class DxfExporter + { + private const double RadToDeg = 180.0 / Math.PI; + + private DxfDocument doc; + private Vector2 curpos; + private Mode mode; + private readonly Layer cutLayer; + private readonly Layer rapidLayer; + private readonly Layer plateLayer; + + public DxfExporter() + { + doc = new DxfDocument(); + + cutLayer = new Layer("Cut"); + cutLayer.Color = AciColor.Red; + + rapidLayer = new Layer("Rapid"); + rapidLayer.Color = AciColor.Blue; + rapidLayer.LineType = LineType.Dashed; + + plateLayer = new Layer("Plate"); + plateLayer.Color = AciColor.Cyan; + } + + public void ExportProgram(Program program, Stream stream) + { + doc = new DxfDocument(); + AddProgram(program); + doc.Save(stream); + } + + public bool ExportProgram(Program program, string path) + { + Stream stream = null; + bool success = false; + + try + { + stream = File.Create(path); + ExportProgram(program, stream); + success = true; + } + catch + { + Debug.Fail("DxfExporter.ExportProgram failed to write program to file: " + path); + } + finally + { + if (stream != null) + stream.Close(); + } + + return success; + } + + public void ExportPlate(Plate plate, Stream stream) + { + doc = new DxfDocument(); + AddPlateOutline(plate); + + foreach (var part in plate.Parts) + { + var endpt = part.Location.ToNetDxf(); + var line = new netDxf.Entities.Line(curpos, endpt); + line.Layer = rapidLayer; + doc.AddEntity(line); + curpos = part.Location.ToNetDxf(); + AddProgram(part.Program); + } + + doc.Save(stream); + } + + public bool ExportPlate(Plate plate, string path) + { + Stream stream = null; + bool success = false; + + try + { + stream = File.Create(path); + ExportPlate(plate, stream); + success = true; + } + catch + { + Debug.Fail("DxfExporter.ExportPlate failed to write plate to file: " + path); + } + finally + { + if (stream != null) + stream.Close(); + } + + return success; + } + + private void AddPlateOutline(Plate plate) + { + Vector2 pt1; + Vector2 pt2; + Vector2 pt3; + Vector2 pt4; + + switch (plate.Quadrant) + { + case 1: + pt1 = new Vector2(0, 0); + pt2 = new Vector2(0, plate.Size.Height); + pt3 = new Vector2(plate.Size.Width, plate.Size.Height); + pt4 = new Vector2(plate.Size.Width, 0); + break; + + case 2: + pt1 = new Vector2(0, 0); + pt2 = new Vector2(0, plate.Size.Height); + pt3 = new Vector2(-plate.Size.Width, plate.Size.Height); + pt4 = new Vector2(-plate.Size.Width, 0); + break; + + case 3: + pt1 = new Vector2(0, 0); + pt2 = new Vector2(0, -plate.Size.Height); + pt3 = new Vector2(-plate.Size.Width, -plate.Size.Height); + pt4 = new Vector2(-plate.Size.Width, 0); + break; + + case 4: + pt1 = new Vector2(0, 0); + pt2 = new Vector2(0, -plate.Size.Height); + pt3 = new Vector2(plate.Size.Width, -plate.Size.Height); + pt4 = new Vector2(plate.Size.Width, 0); + break; + + default: + return; + } + + doc.AddEntity(new Line(pt1, pt2) { Layer = plateLayer }); + doc.AddEntity(new Line(pt2, pt3) { Layer = plateLayer }); + doc.AddEntity(new Line(pt3, pt4) { Layer = plateLayer }); + doc.AddEntity(new Line(pt4, pt1) { Layer = plateLayer }); + + pt1.X += plate.EdgeSpacing.Left; + pt1.Y += plate.EdgeSpacing.Bottom; + + pt2.X = pt1.X; + pt2.Y -= plate.EdgeSpacing.Top; + + pt3.X -= plate.EdgeSpacing.Right; + pt3.Y = pt2.Y; + + pt4.X = pt3.X; + pt4.Y = pt1.Y; + + doc.AddEntity(new Line(pt1, pt2) { Layer = plateLayer, LineType = LineType.Dashed }); + doc.AddEntity(new Line(pt2, pt3) { Layer = plateLayer, LineType = LineType.Dashed }); + doc.AddEntity(new Line(pt3, pt4) { Layer = plateLayer, LineType = LineType.Dashed }); + doc.AddEntity(new Line(pt4, pt1) { Layer = plateLayer, LineType = LineType.Dashed }); + } + + private void AddProgram(Program program) + { + mode = program.Mode; + + for (int i = 0; i < program.Length; ++i) + { + var code = program[i]; + + switch (code.Type) + { + case CodeType.CircularMove: + var arc = (CircularMove)code; + AddCircularMove(arc); + break; + + case CodeType.LinearMove: + var line = (LinearMove)code; + AddLinearMove(line); + break; + + case CodeType.RapidMove: + var rapid = (RapidMove)code; + AddRapidMove(rapid); + break; + + case CodeType.SubProgramCall: + var tmpmode = mode; + var subpgm = (CNC.SubProgramCall)code; + AddProgram(subpgm.Program); + mode = tmpmode; + break; + } + } + } + + private void AddLinearMove(LinearMove line) + { + var pt = line.EndPoint.ToNetDxf(); + + if (mode == Mode.Incremental) + pt += curpos; + + var ln = new Line(curpos, pt); + ln.Layer = cutLayer; + doc.AddEntity(ln); + curpos = pt; + } + + private void AddRapidMove(RapidMove rapid) + { + var pt = rapid.EndPoint.ToNetDxf(); + + if (mode == Mode.Incremental) + pt += curpos; + + var ln = new Line(curpos, pt); + ln.Layer = rapidLayer; + doc.AddEntity(ln); + curpos = pt; + } + + private void AddCircularMove(CircularMove arc) + { + var center = arc.CenterPoint.ToNetDxf(); + var endpt = arc.EndPoint.ToNetDxf(); + + if (mode == Mode.Incremental) + { + endpt += curpos; + center += curpos; + } + + // start angle in radians + var startAngle = Math.Atan2( + curpos.Y - center.Y, + curpos.X - center.X); + + // end angle in radians + var endAngle = Math.Atan2( + endpt.Y - center.Y, + endpt.X - center.X); + + // convert the angles to degrees + startAngle = Angle.ToDegrees(startAngle); + endAngle = Angle.ToDegrees(endAngle); + + if (arc.Rotation == OpenNest.RotationType.CW) + Generic.Swap(ref startAngle, ref endAngle); + + var dx = endpt.X - center.X; + var dy = endpt.Y - center.Y; + + var radius = Math.Sqrt(dx * dx + dy * dy); + + if (startAngle.IsEqualTo(endAngle)) + { + var circle = new Circle(center, radius); + circle.Layer = cutLayer; + doc.AddEntity(circle); + } + else + { + var arc2 = new Arc(center, radius, startAngle, endAngle); + arc2.Layer = cutLayer; + doc.AddEntity(arc2); + } + + curpos = endpt; + } + } +} diff --git a/Source/OpenNest/IO/DxfImporter.cs b/Source/OpenNest/IO/DxfImporter.cs new file mode 100644 index 0000000..fba8051 --- /dev/null +++ b/Source/OpenNest/IO/DxfImporter.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using netDxf; +using OpenNest.Geometry; + +namespace OpenNest.IO +{ + public class DxfImporter + { + public int SplinePrecision { get; set; } + + public DxfImporter() + { + } + + private List GetGeometry(DxfDocument doc) + { + var entities = new List(); + var lines = new List(doc.Lines.Count); + var arcs = new List(doc.Arcs.Count); + + foreach (var spline in doc.Splines) + lines.AddRange(spline.ToOpenNest(SplinePrecision)); + + foreach (var polyline in doc.LwPolylines) + lines.AddRange(polyline.ToOpenNest()); + + foreach (var ellipse in doc.Ellipses) + lines.AddRange(ellipse.ToOpenNest(SplinePrecision)); + + foreach (var line in doc.Lines) + lines.Add(line.ToOpenNest()); + + foreach (var arc in doc.Arcs) + arcs.Add(arc.ToOpenNest()); + + foreach (var circle in doc.Circles) + entities.Add(circle.ToOpenNest()); + + foreach (var polyline in doc.Polylines) + lines.AddRange(polyline.ToOpenNest()); + + Helper.Optimize(lines); + Helper.Optimize(arcs); + + entities.AddRange(lines); + entities.AddRange(arcs); + + return entities; + } + + public bool GetGeometry(Stream stream, out List geometry) + { + bool success = false; + + try + { + var doc = DxfDocument.Load(stream); + geometry = GetGeometry(doc); + success = true; + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + geometry = new List(); + } + finally + { + if (stream != null) + stream.Close(); + } + + return success; + } + + public bool GetGeometry(string path, out List geometry) + { + Stream stream = null; + bool success = false; + + try + { + var doc = DxfDocument.Load(path); + geometry = GetGeometry(doc); + success = true; + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + geometry = new List(); + } + finally + { + if (stream != null) + stream.Close(); + } + + return success; + } + } +} diff --git a/Source/OpenNest/IO/Extensions.cs b/Source/OpenNest/IO/Extensions.cs new file mode 100644 index 0000000..4738d3d --- /dev/null +++ b/Source/OpenNest/IO/Extensions.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; +using System.Linq; +using OpenNest.Geometry; + +namespace OpenNest.IO +{ + internal static class Extensions + { + public static Vector ToOpenNest(this netDxf.Vector2 v) + { + return new Vector(v.X, v.Y); + } + + public static Vector ToOpenNest(this netDxf.Vector3 v) + { + return new Vector(v.X, v.Y); + } + + public static Vector ToOpenNest(this netDxf.Entities.PolylineVertex v) + { + return new Vector(v.Location.X, v.Location.Y); + } + + public static Vector ToOpenNest(this netDxf.Entities.LwPolylineVertex v) + { + return new Vector(v.Location.X, v.Location.Y); + } + + public static Arc ToOpenNest(this netDxf.Entities.Arc arc) + { + return new Arc( + arc.Center.X, arc.Center.Y, arc.Radius, + Angle.ToRadians(arc.StartAngle), + Angle.ToRadians(arc.EndAngle)) + { + Layer = arc.Layer.ToOpenNest() + }; + } + + public static Circle ToOpenNest(this netDxf.Entities.Circle circle) + { + return new Circle( + circle.Center.X, circle.Center.Y, + circle.Radius) + { + Layer = circle.Layer.ToOpenNest() + }; + } + + public static Line ToOpenNest(this netDxf.Entities.Line line) + { + return new Line( + line.StartPoint.X, line.StartPoint.Y, + line.EndPoint.X, line.EndPoint.Y) + { + Layer = line.Layer.ToOpenNest() + }; + } + + public static List ToOpenNest(this netDxf.Entities.Spline spline, int precision = 200) + { + var lines = new List(); + var pts = spline.PolygonalVertexes(precision); + + if (pts.Count == 0) + return lines; + + var lastPoint = pts[0].ToOpenNest(); + + for (int i = 1; i < pts.Count; i++) + { + var nextPoint = pts[i].ToOpenNest(); + + lines.Add(new Line( + lastPoint, + nextPoint) { Layer = spline.Layer.ToOpenNest() }); + + lastPoint = nextPoint; + } + + if (spline.IsClosed) + lines.Add(new Line(lastPoint, pts[0].ToOpenNest()) { Layer = spline.Layer.ToOpenNest() }); + + return lines; + } + + public static List ToOpenNest(this netDxf.Entities.Polyline polyline) + { + var lines = new List(); + + if (polyline.Vertexes.Count == 0) + return lines; + + var lastPoint = polyline.Vertexes[0].ToOpenNest(); + + for (int i = 1; i < polyline.Vertexes.Count; i++) + { + var nextPoint = polyline.Vertexes[i].ToOpenNest(); + + lines.Add(new Line( + lastPoint, + nextPoint) { Layer = polyline.Layer.ToOpenNest() }); + + lastPoint = nextPoint; + } + + if (polyline.IsClosed) + lines.Add(new Line(lastPoint, polyline.Vertexes[0].ToOpenNest()) { Layer = polyline.Layer.ToOpenNest() }); + + return lines; + } + + public static List ToOpenNest(this netDxf.Entities.LwPolyline polyline) + { + var lines = new List(); + + if (polyline.Vertexes.Count == 0) + return lines; + + var lastPoint = polyline.Vertexes[0].ToOpenNest(); + + for (int i = 1; i < polyline.Vertexes.Count; i++) + { + var nextPoint = polyline.Vertexes[i].ToOpenNest(); + + lines.Add(new Line( + lastPoint, + nextPoint) { Layer = polyline.Layer.ToOpenNest() }); + + lastPoint = nextPoint; + } + + if (polyline.IsClosed) + lines.Add(new Line(lastPoint, polyline.Vertexes[0].ToOpenNest()) { Layer = polyline.Layer.ToOpenNest() }); + + return lines; + } + + public static List ToOpenNest(this netDxf.Entities.Ellipse ellipse, int precision = 200) + { + var lines = ellipse.ToPolyline(precision).ToOpenNest(); + + if (lines.Count < 2) + return lines; + + var first = lines.First(); + var last = lines.Last(); + + // workaround for ellipse.ToPolyline not connecting the last and first point. + lines.Add(new Line(last.EndPoint, first.StartPoint) { Layer = ellipse.Layer.ToOpenNest() }); + + return lines; + } + + public static Layer ToOpenNest(this netDxf.Tables.Layer layer) + { + return new Layer(layer.Name) + { + Color = layer.Color.ToColor(), + IsVisible = layer.IsVisible + }; + } + + public static netDxf.Vector2 ToNetDxf(this Vector v) + { + return new netDxf.Vector2(v.X, v.Y); + } + } +} diff --git a/Source/OpenNest/IO/NestReader.cs b/Source/OpenNest/IO/NestReader.cs new file mode 100644 index 0000000..c3355a9 --- /dev/null +++ b/Source/OpenNest/IO/NestReader.cs @@ -0,0 +1,466 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using Ionic.Zip; +using OpenNest.CNC; + +namespace OpenNest.IO +{ + public sealed class NestReader + { + private ZipFile zipFile; + private Dictionary plateDict; + private Dictionary drawingDict; + private Dictionary programDict; + private Dictionary plateProgramDict; + private Stream stream; + private Nest nest; + + private NestReader() + { + plateDict = new Dictionary(); + drawingDict = new Dictionary(); + programDict = new Dictionary(); + plateProgramDict = new Dictionary(); + nest = new Nest(); + } + + public NestReader(string file) + : this() + { + stream = new FileStream(file, FileMode.Open, FileAccess.ReadWrite); + zipFile = ZipFile.Read(stream); + } + + public NestReader(Stream stream) + : this() + { + zipFile = ZipFile.Read(stream); + } + + public Nest Read() + { + const string plateExtensionPattern = "plate-\\d\\d\\d"; + const string programExtensionPattern = "program-\\d\\d\\d"; + + foreach (var entry in zipFile.Entries) + { + var memstream = new MemoryStream(); + entry.Extract(memstream); + + memstream.Position = 0; + + switch (entry.FileName) + { + case "info": + ReadNestInfo(memstream); + continue; + + case "drawing-info": + ReadDrawingInfo(memstream); + continue; + + case "plate-info": + ReadPlateInfo(memstream); + continue; + } + + if (Regex.IsMatch(entry.FileName, programExtensionPattern)) + { + ReadProgram(memstream, entry.FileName); + continue; + } + + if (Regex.IsMatch(entry.FileName, plateExtensionPattern)) + { + ReadPlate(memstream, entry.FileName); + continue; + } + } + + LinkProgramsToDrawings(); + LinkPartsToPlates(); + + AddPlatesToNest(); + AddDrawingsToNest(); + + stream.Close(); + + return nest; + } + + private void ReadNestInfo(Stream stream) + { + var reader = XmlReader.Create(stream); + var spacing = new Spacing(); + + while (reader.Read()) + { + if (!reader.IsStartElement()) + continue; + + switch (reader.Name) + { + case "Nest": + nest.Name = reader["name"]; + break; + + case "Units": + Units units; + TryParseEnum(reader.ReadString(), out units); + nest.Units = units; + break; + + case "Customer": + nest.Customer = reader.ReadString(); + break; + + case "DateCreated": + nest.DateCreated = DateTime.Parse(reader.ReadString()); + break; + + case "DateLastModified": + nest.DateLastModified = DateTime.Parse(reader.ReadString()); + break; + + case "Notes": + nest.Notes = Uri.UnescapeDataString(reader.ReadString()); + break; + + case "Size": + nest.PlateDefaults.Size = Size.Parse(reader.ReadString()); + break; + + case "Thickness": + nest.PlateDefaults.Thickness = double.Parse(reader.ReadString()); + break; + + case "Quadrant": + nest.PlateDefaults.Quadrant = int.Parse(reader.ReadString()); + break; + + case "PartSpacing": + nest.PlateDefaults.PartSpacing = double.Parse(reader.ReadString()); + break; + + case "Name": + nest.PlateDefaults.Material.Name = reader.ReadString(); + break; + + case "Grade": + nest.PlateDefaults.Material.Grade = reader.ReadString(); + break; + + case "Density": + nest.PlateDefaults.Material.Density = double.Parse(reader.ReadString()); + break; + + case "Left": + spacing.Left = double.Parse(reader.ReadString()); + break; + + case "Right": + spacing.Right = double.Parse(reader.ReadString()); + break; + + case "Top": + spacing.Top = double.Parse(reader.ReadString()); + break; + + case "Bottom": + spacing.Bottom = double.Parse(reader.ReadString()); + break; + } + } + + reader.Close(); + nest.PlateDefaults.EdgeSpacing = spacing; + } + + private void ReadDrawingInfo(Stream stream) + { + var reader = XmlReader.Create(stream); + Drawing drawing = null; + + while (reader.Read()) + { + if (!reader.IsStartElement()) + continue; + + switch (reader.Name) + { + case "Drawing": + var id = int.Parse(reader["id"]); + var name = reader["name"]; + + drawingDict.Add(id, (drawing = new Drawing(name))); + break; + + case "Customer": + drawing.Customer = reader.ReadString(); + break; + + case "Color": + { + var parts = reader.ReadString().Split(','); + + if (parts.Length == 3) + { + byte r = byte.Parse(parts[0]); + byte g = byte.Parse(parts[1]); + byte b = byte.Parse(parts[2]); + + drawing.Color = Color.FromArgb(r, g, b); + } + else if (parts.Length == 4) + { + byte a = byte.Parse(parts[0]); + byte r = byte.Parse(parts[1]); + byte g = byte.Parse(parts[2]); + byte b = byte.Parse(parts[3]); + + drawing.Color = Color.FromArgb(a, r, g, b); + } + } + break; + + case "Required": + drawing.Quantity.Required = int.Parse(reader.ReadString()); + break; + + case "Name": + drawing.Material.Name = reader.ReadString(); + break; + + case "Grade": + drawing.Material.Grade = reader.ReadString(); + break; + + case "Density": + drawing.Material.Density = double.Parse(reader.ReadString()); + break; + + case "Path": + drawing.Source.Path = reader.ReadString(); + break; + + case "Offset": + { + var parts = reader.ReadString().Split(','); + + if (parts.Length != 2) + continue; + + drawing.Source.Offset = new Vector(double.Parse(parts[0]), double.Parse(parts[1])); + } + break; + } + } + + reader.Close(); + } + + private void ReadPlateInfo(Stream stream) + { + var reader = XmlReader.Create(stream); + var spacing = new Spacing(); + Plate plate = null; + + while (reader.Read()) + { + if (!reader.IsStartElement()) + continue; + + switch (reader.Name) + { + case "Plate": + var id = int.Parse(reader["id"]); + + if (plate != null) + plate.EdgeSpacing = spacing; + + plateDict.Add(id, (plate = new Plate())); + break; + + case "Size": + plate.Size = Size.Parse(reader.ReadString()); + break; + + case "Qty": + plate.Quantity = int.Parse(reader.ReadString()); + break; + + case "Thickness": + plate.Thickness = double.Parse(reader.ReadString()); + break; + + case "Quadrant": + plate.Quadrant = int.Parse(reader.ReadString()); + break; + + case "PartSpacing": + plate.PartSpacing = double.Parse(reader.ReadString()); + break; + + case "Name": + plate.Material.Name = reader.ReadString(); + break; + + case "Grade": + plate.Material.Grade = reader.ReadString(); + break; + + case "Density": + plate.Material.Density = double.Parse(reader.ReadString()); + break; + + case "Left": + spacing.Left = double.Parse(reader.ReadString()); + break; + + case "Right": + spacing.Right = double.Parse(reader.ReadString()); + break; + + case "Top": + spacing.Top = double.Parse(reader.ReadString()); + break; + + case "Bottom": + spacing.Bottom = double.Parse(reader.ReadString()); + break; + } + } + + if (plate != null) + plate.EdgeSpacing = spacing; + } + + private void ReadProgram(Stream stream, string name) + { + var id = GetProgramId(name); + var reader = new ProgramReader(stream); + var pgm = reader.Read(); + programDict.Add(id, pgm); + } + + private void ReadPlate(Stream stream, string name) + { + var id = GetPlateId(name); + var reader = new ProgramReader(stream); + var pgm = reader.Read(); + plateProgramDict.Add(id, pgm); + } + + private void LinkProgramsToDrawings() + { + foreach (var drawingItem in drawingDict) + { + Program pgm; + + if (programDict.TryGetValue(drawingItem.Key, out pgm)) + drawingItem.Value.Program = pgm; + } + } + + private void LinkPartsToPlates() + { + foreach (var plateProgram in plateProgramDict) + { + var parts = CreateParts(plateProgram.Value); + + Plate plate; + + if (!plateDict.TryGetValue(plateProgram.Key, out plate)) + plate = new Plate(); + + plate.Parts.AddRange(parts); + plateDict[plateProgram.Key] = plate; + } + } + + private void AddPlatesToNest() + { + var plates = plateDict.OrderBy(i => i.Key).Select(i => i.Value).ToList(); + nest.Plates.AddRange(plates); + } + + private void AddDrawingsToNest() + { + var drawings = drawingDict.OrderBy(i => i.Key).Select(i => i.Value).ToList(); + drawings.ForEach(d => nest.Drawings.Add(d)); + } + + private List CreateParts(Program pgm) + { + var parts = new List(); + var pos = Vector.Zero; + + for (int i = 0; i < pgm.Codes.Count; i++) + { + var code = pgm.Codes[i]; + + switch (code.Type) + { + case CodeType.RapidMove: + pos = ((RapidMove)code).EndPoint; + break; + + case CodeType.SubProgramCall: + var subpgm = (SubProgramCall)code; + var dwg = drawingDict[subpgm.Id]; + var part = new Part(dwg); + part.Rotate(Angle.ToRadians(subpgm.Rotation)); + part.Offset(pos); + parts.Add(part); + break; + } + } + + return parts; + } + + private int GetPlateId(string name) + { + return int.Parse(name.Replace("plate-", "")); + } + + private int GetProgramId(string name) + { + return int.Parse(name.Replace("program-", "")); + } + + public static T ParseEnum(string value) + { + return (T)Enum.Parse(typeof(T), value, true); + } + + public static bool TryParseEnum(string value, out T e) + { + try + { + e = ParseEnum(value); + return true; + } + catch + { + e = ParseEnum(typeof(T).GetEnumValues().GetValue(0).ToString()); + } + + return false; + } + + private enum NestInfoSection + { + None, + DefaultPlate, + Material, + EdgeSpacing, + Source + } + } +} diff --git a/Source/OpenNest/IO/NestWriter.cs b/Source/OpenNest/IO/NestWriter.cs new file mode 100644 index 0000000..b80b783 --- /dev/null +++ b/Source/OpenNest/IO/NestWriter.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Ionic.Zip; +using OpenNest.CNC; + +namespace OpenNest.IO +{ + public sealed class NestWriter + { + /// + /// Number of decimal places the output is round to. + /// This number must have more decimal places than Tolerance.Epsilon + /// + private const int OutputPrecision = 10; + + private readonly Nest nest; + private ZipFile zipFile; + private Dictionary drawingDict; + + public NestWriter(Nest nest) + { + this.drawingDict = new Dictionary(); + this.nest = nest; + } + + public bool Write(string file) + { + this.nest.DateLastModified = DateTime.Now; + + SetDrawingIds(); + zipFile = new ZipFile(); + AddNestInfo(); + AddPlates(); + AddPlateInfo(); + AddDrawings(); + AddDrawingInfo(); + zipFile.Save(file); + + return true; + } + + private void SetDrawingIds() + { + int id = 1; + + foreach (var drawing in nest.Drawings) + { + drawingDict.Add(id, drawing); + id++; + } + } + + private void AddNestInfo() + { + var stream = new MemoryStream(); + var writer = XmlWriter.Create(stream, new XmlWriterSettings() + { + Indent = true + }); + + writer.WriteStartDocument(); + writer.WriteStartElement("Nest"); + writer.WriteAttributeString("name", nest.Name); + + writer.WriteElementString("Units", nest.Units.ToString()); + writer.WriteElementString("Customer", nest.Customer); + writer.WriteElementString("DateCreated", nest.DateCreated.ToString()); + writer.WriteElementString("DateLastModified", nest.DateLastModified.ToString()); + + writer.WriteStartElement("DefaultPlate"); + writer.WriteElementString("Size", nest.PlateDefaults.Size.ToString()); + writer.WriteElementString("Thickness", nest.PlateDefaults.Thickness.ToString()); + writer.WriteElementString("Quadrant", nest.PlateDefaults.Quadrant.ToString()); + writer.WriteElementString("PartSpacing", nest.PlateDefaults.PartSpacing.ToString()); + + writer.WriteStartElement("Material"); + writer.WriteElementString("Name", nest.PlateDefaults.Material.Name); + writer.WriteElementString("Grade", nest.PlateDefaults.Material.Grade); + writer.WriteElementString("Density", nest.PlateDefaults.Material.Density.ToString()); + writer.WriteEndElement(); + + writer.WriteStartElement("EdgeSpacing"); + writer.WriteElementString("Left", nest.PlateDefaults.EdgeSpacing.Left.ToString()); + writer.WriteElementString("Top", nest.PlateDefaults.EdgeSpacing.Top.ToString()); + writer.WriteElementString("Right", nest.PlateDefaults.EdgeSpacing.Right.ToString()); + writer.WriteElementString("Bottom", nest.PlateDefaults.EdgeSpacing.Bottom.ToString()); + writer.WriteEndElement(); + + writer.WriteElementString("Notes", Uri.EscapeDataString(nest.Notes)); + + writer.WriteEndElement(); // DefaultPlate + writer.WriteEndElement(); // Nest + + writer.WriteEndDocument(); + + writer.Flush(); + writer.Close(); + + stream.Position = 0; + + zipFile.AddEntry("info", stream); + } + + private void AddPlates() + { + int num = 1; + + foreach (var plate in nest.Plates) + { + var stream = new MemoryStream(); + var name = string.Format("plate-{0}", num.ToString().PadLeft(3, '0')); + + WritePlate(stream, plate); + zipFile.AddEntry(name, stream); + + num++; + } + } + + private void AddPlateInfo() + { + var stream = new MemoryStream(); + var writer = XmlWriter.Create(stream, new XmlWriterSettings() + { + Indent = true + }); + + writer.WriteStartDocument(); + writer.WriteStartElement("Plates"); + writer.WriteAttributeString("count", nest.Plates.Count.ToString()); + + for (int i = 0; i < nest.Plates.Count; ++i) + { + var plate = nest.Plates[i]; + + writer.WriteStartElement("Plate"); + writer.WriteAttributeString("id", (i + 1).ToString()); + + writer.WriteElementString("Quadrant", plate.Quadrant.ToString()); + writer.WriteElementString("Thickness", plate.Thickness.ToString()); + writer.WriteElementString("Size", plate.Size.ToString()); + writer.WriteElementString("Qty", plate.Quantity.ToString()); + writer.WriteElementString("PartSpacing", plate.PartSpacing.ToString()); + + writer.WriteStartElement("Material"); + writer.WriteElementString("Name", plate.Material.Name); + writer.WriteElementString("Grade", plate.Material.Grade); + writer.WriteElementString("Density", plate.Material.Density.ToString()); + writer.WriteEndElement(); + + writer.WriteStartElement("EdgeSpacing"); + writer.WriteElementString("Left", plate.EdgeSpacing.Left.ToString()); + writer.WriteElementString("Top", plate.EdgeSpacing.Top.ToString()); + writer.WriteElementString("Right", plate.EdgeSpacing.Right.ToString()); + writer.WriteElementString("Bottom", plate.EdgeSpacing.Bottom.ToString()); + writer.WriteEndElement(); + + writer.WriteEndElement(); // Plate + writer.Flush(); + } + + writer.WriteEndElement(); // Plates + writer.WriteEndDocument(); + + writer.Flush(); + writer.Close(); + + stream.Position = 0; + + zipFile.AddEntry("plate-info", stream); + } + + private void AddDrawings() + { + int num = 1; + + foreach (var dwg in nest.Drawings) + { + var stream = new MemoryStream(); + var name = string.Format("program-{0}", num.ToString().PadLeft(3, '0')); + + WriteDrawing(stream, dwg); + zipFile.AddEntry(name, stream); + + num++; + } + } + + private void AddDrawingInfo() + { + var stream = new MemoryStream(); + var writer = XmlWriter.Create(stream, new XmlWriterSettings() + { + Indent = true + }); + + writer.WriteStartDocument(); + writer.WriteStartElement("Drawings"); + writer.WriteAttributeString("count", nest.Drawings.Count.ToString()); + + int id = 1; + + foreach (var drawing in nest.Drawings) + { + writer.WriteStartElement("Drawing"); + writer.WriteAttributeString("id", id.ToString()); + writer.WriteAttributeString("name", drawing.Name); + + writer.WriteElementString("Customer", drawing.Customer); + writer.WriteElementString("Color", string.Format("{0}, {1}, {2}, {3}", drawing.Color.A, drawing.Color.R, drawing.Color.G, drawing.Color.B)); + + writer.WriteStartElement("Quantity"); + writer.WriteElementString("Required", drawing.Quantity.Required.ToString()); + writer.WriteElementString("Nested", drawing.Quantity.Nested.ToString()); + writer.WriteEndElement(); + + writer.WriteStartElement("Material"); + writer.WriteElementString("Name", drawing.Material.Name); + writer.WriteElementString("Grade", drawing.Material.Grade); + writer.WriteElementString("Density", drawing.Material.Density.ToString()); + writer.WriteEndElement(); + + writer.WriteStartElement("Source"); + writer.WriteElementString("Path", drawing.Source.Path); + writer.WriteElementString("Offset", string.Format("{0}, {1}", + drawing.Source.Offset.X, + drawing.Source.Offset.Y)); + writer.WriteEndElement(); // Source + + writer.WriteEndElement(); // Drawing + + id++; + } + + writer.WriteEndElement(); // Drawings + writer.WriteEndDocument(); + + writer.Flush(); + writer.Close(); + + stream.Position = 0; + + zipFile.AddEntry("drawing-info", stream); + } + + private void WritePlate(Stream stream, Plate plate) + { + var writer = new StreamWriter(stream); + writer.AutoFlush = true; + writer.WriteLine("G90"); + + foreach (var part in plate.Parts) + { + var match = drawingDict.Where(dwg => dwg.Value == part.BaseDrawing).FirstOrDefault(); + var id = match.Key; + + writer.WriteLine("G00X{0}Y{1}", part.Location.X, part.Location.Y); + writer.WriteLine("G65P{0}R{1}", id, Angle.ToDegrees(part.Rotation)); + } + + stream.Position = 0; + } + + private void WriteDrawing(Stream stream, Drawing drawing) + { + var program = drawing.Program; + var writer = new StreamWriter(stream); + writer.AutoFlush = true; + + writer.WriteLine(program.Mode == Mode.Absolute ? "G90" : "G91"); + + for (int i = 0; i < drawing.Program.Length; ++i) + { + var code = drawing.Program[i]; + writer.WriteLine(GetCodeString(code)); + } + + stream.Position = 0; + } + + private string GetCodeString(ICode code) + { + switch (code.Type) + { + case CodeType.CircularMove: + { + var sb = new StringBuilder(); + var circularMove = (CircularMove)code; + + if (circularMove.Rotation == RotationType.CW) + { + sb.Append(string.Format("G02X{0}Y{1}I{2}J{3}", + Math.Round(circularMove.EndPoint.X, OutputPrecision), + Math.Round(circularMove.EndPoint.Y, OutputPrecision), + Math.Round(circularMove.CenterPoint.X, OutputPrecision), + Math.Round(circularMove.CenterPoint.Y, OutputPrecision))); + } + else + { + sb.Append(string.Format("G03X{0}Y{1}I{2}J{3}", + Math.Round(circularMove.EndPoint.X, OutputPrecision), + Math.Round(circularMove.EndPoint.Y, OutputPrecision), + Math.Round(circularMove.CenterPoint.X, OutputPrecision), + Math.Round(circularMove.CenterPoint.Y, OutputPrecision))); + } + + if (circularMove.Layer != LayerType.Cut) + sb.Append(GetLayerString(circularMove.Layer)); + + return sb.ToString(); + } + + case CodeType.Comment: + { + var comment = (Comment)code; + return ":" + comment.Value; + } + + case CodeType.LinearMove: + { + var sb = new StringBuilder(); + var linearMove = (LinearMove)code; + + sb.Append(string.Format("G01X{0}Y{1}", + Math.Round(linearMove.EndPoint.X, OutputPrecision), + Math.Round(linearMove.EndPoint.Y, OutputPrecision))); + + if (linearMove.Layer != LayerType.Cut) + sb.Append(GetLayerString(linearMove.Layer)); + + return sb.ToString(); + } + + case CodeType.RapidMove: + { + var rapidMove = (RapidMove)code; + + return string.Format("G00X{0}Y{1}", + Math.Round(rapidMove.EndPoint.X, OutputPrecision), + Math.Round(rapidMove.EndPoint.Y, OutputPrecision)); + } + + case CodeType.SetFeedrate: + { + var setFeedrate = (Feedrate)code; + return "F" + setFeedrate.Value; + } + + case CodeType.SetKerf: + { + var setKerf = (Kerf)code; + + switch (setKerf.Value) + { + case KerfType.None: return "G40"; + case KerfType.Left: return "G41"; + case KerfType.Right: return "G42"; + } + + break; + } + + case CodeType.SubProgramCall: + { + var subProgramCall = (SubProgramCall)code; + break; + } + } + + return string.Empty; + } + + private string GetLayerString(LayerType layer) + { + switch (layer) + { + case LayerType.Display: + return ":DISPLAY"; + + case LayerType.Leadin: + return ":LEADIN"; + + case LayerType.Leadout: + return ":LEADOUT"; + + case LayerType.Scribe: + return ":SCRIBE"; + + default: + return string.Empty; + } + } + } +} diff --git a/Source/OpenNest/IO/ProgramReader.cs b/Source/OpenNest/IO/ProgramReader.cs new file mode 100644 index 0000000..3f7b182 --- /dev/null +++ b/Source/OpenNest/IO/ProgramReader.cs @@ -0,0 +1,389 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using OpenNest.CNC; + +namespace OpenNest.IO +{ + internal sealed class ProgramReader + { + private const int BufferSize = 200; + + private int codeIndex; + private CodeBlock block; + private CodeSection section; + private Program program; + private StreamReader reader; + + public ProgramReader(Stream stream) + { + reader = new StreamReader(stream); + program = new Program(); + } + + public Program Read() + { + string line; + + while ((line = reader.ReadLine()) != null) + { + block = ParseBlock(line); + ProcessCurrentBlock(); + } + + return program; + } + + private CodeBlock ParseBlock(string line) + { + var block = new CodeBlock(); + Code code = null; + for (int i = 0; i < line.Length; ++i) + { + var c = line[i]; + if (char.IsLetter(c)) + block.Add((code = new Code(c))); + else if (c == ':') + { + block.Add((new Code(c, line.Remove(0, i + 1).Trim()))); + break; + } + else if (code != null) + code.Value += c; + } + return block; + } + + private void ProcessCurrentBlock() + { + var code = GetFirstCode(); + + while (code != null) + { + switch (code.Id) + { + case ':': + program.Codes.Add(new Comment(code.Value)); + code = GetNextCode(); + break; + + case 'G': + int value = int.Parse(code.Value); + + switch (value) + { + case 0: + case 1: + section = CodeSection.Line; + ReadLine(value == 0); + code = GetCurrentCode(); + break; + + case 2: + case 3: + section = CodeSection.Arc; + ReadArc(value == 2 ? RotationType.CW : RotationType.CCW); + code = GetCurrentCode(); + break; + + case 65: + section = CodeSection.SubProgram; + ReadSubProgram(); + code = GetCurrentCode(); + break; + + case 40: + program.Codes.Add(new Kerf() { Value = KerfType.None }); + code = GetNextCode(); + break; + + case 41: + program.Codes.Add(new Kerf() { Value = KerfType.Left }); + code = GetNextCode(); + break; + + case 42: + program.Codes.Add(new Kerf() { Value = KerfType.Right }); + code = GetNextCode(); + break; + + case 90: + program.Mode = Mode.Absolute; + code = GetNextCode(); + break; + + case 91: + program.Mode = Mode.Incremental; + code = GetNextCode(); + break; + + default: + code = GetNextCode(); + break; + } + break; + + case 'F': + program.Codes.Add(new Feedrate() { Value = double.Parse(code.Value) }); + code = GetNextCode(); + break; + + default: + code = GetNextCode(); + break; + } + } + } + + private void ReadLine(bool isRapid) + { + var line = new LinearMove(); + double x = 0; + double y = 0; + var layer = LayerType.Cut; + + while (section == CodeSection.Line) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + switch (code.Id) + { + case 'X': + x = double.Parse(code.Value); + break; + + case 'Y': + y = double.Parse(code.Value); + break; + + case ':': + { + var value = code.Value.Trim().ToUpper(); + + switch (value) + { + case "DISPLAY": + layer = LayerType.Display; + break; + + case "LEADIN": + layer = LayerType.Leadin; + break; + + case "LEADOUT": + layer = LayerType.Leadout; + break; + + case "SCRIBE": + layer = LayerType.Scribe; + break; + } + break; + } + + default: + section = CodeSection.Unknown; + break; + } + } + if (isRapid) + program.Codes.Add(new RapidMove(x, y)); + else + program.Codes.Add(new LinearMove(x, y) { Layer = layer }); + } + + private void ReadArc(RotationType rotation) + { + double x = 0; + double y = 0; + double i = 0; + double j = 0; + var layer = LayerType.Cut; + + while (section == CodeSection.Arc) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + + switch (code.Id) + { + case 'X': + x = double.Parse(code.Value); + break; + + case 'Y': + y = double.Parse(code.Value); + break; + + case 'I': + i = double.Parse(code.Value); + break; + + case 'J': + j = double.Parse(code.Value); + break; + + case ':': + { + var value = code.Value.Trim().ToUpper(); + + switch (value) + { + case "DISPLAY": + layer = LayerType.Display; + break; + + case "LEADIN": + layer = LayerType.Leadin; + break; + + case "LEADOUT": + layer = LayerType.Leadout; + break; + + case "SCRIBE": + layer = LayerType.Scribe; + break; + } + break; + } + + default: + section = CodeSection.Unknown; + break; + } + } + program.Codes.Add(new CircularMove() + { + EndPoint = new Vector(x, y), + CenterPoint = new Vector(i, j), + Rotation = rotation, + Layer = layer + }); + } + + private void ReadSubProgram() + { + var p = 0; + var r = 0.0; + + while (section == CodeSection.SubProgram) + { + var code = GetNextCode(); + + if (code == null) + { + section = CodeSection.Unknown; + break; + } + + switch (code.Id) + { + case 'P': + p = int.Parse(code.Value); + break; + + case 'R': + r = double.Parse(code.Value); + break; + + default: + section = CodeSection.Unknown; + break; + } + } + + program.Codes.Add(new SubProgramCall() { Id = p, Rotation = r }); + } + + private Code GetNextCode() + { + codeIndex++; + + if (codeIndex >= block.Count) + return null; + + return block[codeIndex]; + } + + private Code GetCurrentCode() + { + if (codeIndex >= block.Count) + return null; + + return block[codeIndex]; + } + + private Code GetFirstCode() + { + if (block.Count == 0) + return null; + + codeIndex = 0; + return block[codeIndex]; + } + + public void Close() + { + reader.Close(); + } + + private class Code + { + public Code(char id) + { + Id = id; + Value = string.Empty; + } + + public Code(char id, string value) + { + Id = id; + Value = value; + } + + public char Id { get; private set; } + + public string Value { get; set; } + + public override string ToString() + { + return Id + Value; + } + } + + private class CodeBlock : List + { + public void Add(char id, string value) + { + Add(new Code(id, value)); + } + + public override string ToString() + { + var builder = new StringBuilder(); + foreach (var code in this) + builder.Append(code.ToString() + " "); + return builder.ToString(); + } + } + + private enum CodeSection + { + Unknown, + Arc, + Line, + SubProgram + } + } +} diff --git a/Source/OpenNest/LayoutPart.cs b/Source/OpenNest/LayoutPart.cs new file mode 100644 index 0000000..e34b92d --- /dev/null +++ b/Source/OpenNest/LayoutPart.cs @@ -0,0 +1,202 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using OpenNest.Controls; + +namespace OpenNest +{ + public class LayoutPart : IPart + { + private static Font programIdFont; + private static Color selectedColor; + private static Pen selectedPen; + private static Brush selectedBrush; + + private Color color; + private Brush brush; + private Pen pen; + + public readonly Part BasePart; + + static LayoutPart() + { + programIdFont = new Font(SystemFonts.DefaultFont, FontStyle.Bold | FontStyle.Underline); + SelectedColor = Color.FromArgb(90, 150, 200, 255); + } + + private LayoutPart(Part part) + { + this.BasePart = part; + + if (part.BaseDrawing.Color.IsEmpty) + part.BaseDrawing.Color = Color.FromArgb(130, 204, 130); + + Color = part.BaseDrawing.Color; + } + + internal bool IsDirty { get; set; } + + public bool IsSelected { get; set; } + + public GraphicsPath Path { get; private set; } + + public Color Color + { + get { return color; } + set + { + color = value; + + if (brush != null) + brush.Dispose(); + + brush = new SolidBrush(value); + + if (pen != null) + pen.Dispose(); + + pen = new Pen(ControlPaint.Dark(value)); + } + } + + public void Draw(Graphics g) + { + if (IsSelected) + { + g.FillPath(selectedBrush, Path); + g.DrawPath(selectedPen, Path); + } + else + { + g.FillPath(brush, Path); + g.DrawPath(pen, Path); + } + } + + public void Draw(Graphics g, string id) + { + if (IsSelected) + { + g.FillPath(selectedBrush, Path); + g.DrawPath(selectedPen, Path); + } + else + { + g.FillPath(brush, Path); + g.DrawPath(pen, Path); + } + + var pt = Path.PointCount > 0 ? Path.PathPoints[0] : PointF.Empty; + + g.DrawString(id, programIdFont, Brushes.Black, pt.X, pt.Y); + } + + public void Update(DrawControl plateView) + { + Path = GraphicsHelper.GetGraphicsPath(BasePart.Program, BasePart.Location); + Path.Transform(plateView.Matrix); + IsDirty = false; + } + + public static LayoutPart Create(Part part, PlateView plateView) + { + var layoutPart = new LayoutPart(part); + layoutPart.Update(plateView); + + return layoutPart; + } + + public static Color SelectedColor + { + get { return selectedColor; } + set + { + selectedColor = value; + + if (selectedBrush != null) + selectedBrush.Dispose(); + + selectedBrush = new SolidBrush(value); + + if (selectedPen != null) + selectedPen.Dispose(); + + selectedPen = new Pen(ControlPaint.Dark(value)); + } + } + + public Vector Location + { + get { return BasePart.Location; } + set + { + BasePart.Location = value; + IsDirty = true; + } + } + + public double Rotation + { + get { return BasePart.Rotation; } + } + + public void Rotate(double angle) + { + BasePart.Rotate(angle); + IsDirty = true; + } + + public void Rotate(double angle, Vector origin) + { + BasePart.Rotate(angle, origin); + IsDirty = true; + } + + public void Offset(double x, double y) + { + BasePart.Offset(x, y); + IsDirty = true; + } + + public void Offset(Vector voffset) + { + BasePart.Offset(voffset); + IsDirty = true; + } + + public Box BoundingBox + { + get { return BasePart.BoundingBox; } + } + + public double Left + { + get { return BasePart.Left; } + } + + public double Right + { + get { return BasePart.Right; } + } + + public double Top + { + get { return BasePart.Top; } + } + + public double Bottom + { + get { return BasePart.Bottom; } + } + + public void UpdateBounds() + { + BasePart.UpdateBounds(); + } + + public void Update() + { + Color = BasePart.BaseDrawing.Color; + } + } +} diff --git a/Source/OpenNest/MainApp.cs b/Source/OpenNest/MainApp.cs new file mode 100644 index 0000000..dacba67 --- /dev/null +++ b/Source/OpenNest/MainApp.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using OpenNest.Forms; + +namespace OpenNest +{ + internal static class MainApp + { + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + if (Environment.OSVersion.Version.Major >= 6) + SetProcessDPIAware(); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + + [DllImport("user32.dll")] + private static extern bool SetProcessDPIAware(); + } +} diff --git a/Source/OpenNest/MdiExtensions.cs b/Source/OpenNest/MdiExtensions.cs new file mode 100644 index 0000000..740fd0a --- /dev/null +++ b/Source/OpenNest/MdiExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Windows.Forms; + +namespace OpenNest +{ + public static class MdiExtensions + { + /// + /// Sets the display state of the 3D bevel on MDI client area. + /// Source: http://stackoverflow.com/questions/7752696/how-to-remove-3d-border-sunken-from-mdiclient-component-in-mdi-parent-form + /// + /// + /// + /// + public static bool SetBevel(this Form form, bool show) + { + foreach (Control c in form.Controls) + { + if (c is MdiClient == false) + continue; + + var client = c as MdiClient; + + int windowLong = Win32.GetWindowLong(c.Handle, Win32.GWL_EXSTYLE); + + if (show) + windowLong |= Win32.WS_EX_CLIENTEDGE; + else + windowLong &= ~Win32.WS_EX_CLIENTEDGE; + + Win32.SetWindowLong(c.Handle, Win32.GWL_EXSTYLE, windowLong); + + // Update the non-client area. + Win32.SetWindowPos(client.Handle, IntPtr.Zero, 0, 0, 0, 0, + Win32.SWP_NOACTIVATE | Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_NOZORDER | + Win32.SWP_NOOWNERZORDER | Win32.SWP_FRAMECHANGED); + + return true; + } + return false; + } + } +} diff --git a/Source/OpenNest/OpenNest.csproj b/Source/OpenNest/OpenNest.csproj new file mode 100644 index 0000000..a1dbe59 --- /dev/null +++ b/Source/OpenNest/OpenNest.csproj @@ -0,0 +1,324 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {1F1E40E0-5C53-474F-A258-69C9C3FAC15A} + WinExe + Properties + OpenNest + OpenNest + v4.0 + + + 512 + + + true + bin\Debug\ + TRACE;DEBUG + false + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + + False + ..\..\External\Ionic.Zip.dll + + + False + ..\..\External\netDxf.dll + + + + + + + + + + + + + + + + + + + Component + + + Component + + + Component + + + Component + + + Component + + + Component + + + Component + + + Component + + + Component + + + + Form + + + AutoNestForm.cs + + + Form + + + CadConverterForm.cs + + + Form + + + EditDrawingForm.cs + + + Form + + + EditNestForm.cs + + + Form + + + FillPlateForm.cs + + + Form + + + CutParametersForm.cs + + + Form + + + MainForm.cs + + + Form + + + EditNestInfoForm.cs + + + Form + + + OptionsForm.cs + + + Form + + + EditPlateForm.cs + + + Form + + + SequenceForm.cs + + + Form + + + SetValueForm.cs + + + Form + + + TimingForm.cs + + + + + + + + + + + + + + + + + AutoNestForm.cs + + + CadConverterForm.cs + + + EditDrawingForm.cs + + + EditNestForm.cs + + + FillPlateForm.cs + + + CutParametersForm.cs + + + MainForm.cs + + + EditNestInfoForm.cs + + + OptionsForm.cs + + + EditPlateForm.cs + + + SequenceForm.cs + + + SetValueForm.cs + + + TimingForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {5a5fde8d-f8db-440e-866c-c4807e1686cf} + OpenNest.Core + + + {0083b9cc-54ad-4085-a30d-56bc6834b71a} + OpenNest.Engine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if $(ConfigurationName) == Release ( + xcopy "$(TargetDir)*.*" "$(SolutionDir)\..\Installer\" /Y +) + + + \ No newline at end of file diff --git a/Source/OpenNest/Properties/AssemblyInfo.cs b/Source/OpenNest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1025566 --- /dev/null +++ b/Source/OpenNest/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenNest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenNest")] +[assembly: AssemblyCopyright("Copyright © AJ Isaacs 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f99a0ac7-0b95-4c24-b9bc-f18b64d93242")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.2.0.0")] +[assembly: AssemblyFileVersion("0.2")] diff --git a/Source/OpenNest/Properties/Resources.Designer.cs b/Source/OpenNest/Properties/Resources.Designer.cs new file mode 100644 index 0000000..0a2befe --- /dev/null +++ b/Source/OpenNest/Properties/Resources.Designer.cs @@ -0,0 +1,196 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1022 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenNest.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenNest.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap add { + get { + object obj = ResourceManager.GetObject("add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap clear { + get { + object obj = ResourceManager.GetObject("clear", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap clock { + get { + object obj = ResourceManager.GetObject("clock", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap doc_new { + get { + object obj = ResourceManager.GetObject("doc_new", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap doc_open { + get { + object obj = ResourceManager.GetObject("doc_open", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap import { + get { + object obj = ResourceManager.GetObject("import", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap move_first { + get { + object obj = ResourceManager.GetObject("move_first", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap move_last { + get { + object obj = ResourceManager.GetObject("move_last", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap move_next { + get { + object obj = ResourceManager.GetObject("move_next", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap move_previous { + get { + object obj = ResourceManager.GetObject("move_previous", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap remove { + get { + object obj = ResourceManager.GetObject("remove", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap rotate_ccw { + get { + object obj = ResourceManager.GetObject("rotate_ccw", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap rotate_cw { + get { + object obj = ResourceManager.GetObject("rotate_cw", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap save { + get { + object obj = ResourceManager.GetObject("save", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap save_as { + get { + object obj = ResourceManager.GetObject("save_as", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap watermark { + get { + object obj = ResourceManager.GetObject("watermark", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap zoom_all { + get { + object obj = ResourceManager.GetObject("zoom_all", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap zoom_in { + get { + object obj = ResourceManager.GetObject("zoom_in", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap zoom_out { + get { + object obj = ResourceManager.GetObject("zoom_out", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Source/OpenNest/Properties/Resources.resx b/Source/OpenNest/Properties/Resources.resx new file mode 100644 index 0000000..cbba870 --- /dev/null +++ b/Source/OpenNest/Properties/Resources.resx @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\clear.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\clock.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\doc_new.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\doc_open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\import.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\move_first.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\move_last.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\move_next.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\move_previous.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\rotate_ccw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\rotate_cw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\save_as.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\watermark.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\zoom_all.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\zoom_in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\zoom_out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Source/OpenNest/Properties/Settings.Designer.cs b/Source/OpenNest/Properties/Settings.Designer.cs new file mode 100644 index 0000000..4fc1085 --- /dev/null +++ b/Source/OpenNest/Properties/Settings.Designer.cs @@ -0,0 +1,218 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OpenNest.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Point MainWindowLocation { + get { + return ((global::System.Drawing.Point)(this["MainWindowLocation"])); + } + set { + this["MainWindowLocation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Size MainWindowSize { + get { + return ((global::System.Drawing.Size)(this["MainWindowSize"])); + } + set { + this["MainWindowSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Normal")] + public global::System.Windows.Forms.FormWindowState MainWindowState { + get { + return ((global::System.Windows.Forms.FormWindowState)(this["MainWindowState"])); + } + set { + this["MainWindowState"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool PlateViewDrawRapid { + get { + return ((bool)(this["PlateViewDrawRapid"])); + } + set { + this["PlateViewDrawRapid"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool PlateViewDrawBounds { + get { + return ((bool)(this["PlateViewDrawBounds"])); + } + set { + this["PlateViewDrawBounds"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool CreateNewNestOnOpen { + get { + return ((bool)(this["CreateNewNestOnOpen"])); + } + set { + this["CreateNewNestOnOpen"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0.5")] + public double AutoSizePlateFactor { + get { + return ((double)(this["AutoSizePlateFactor"])); + } + set { + this["AutoSizePlateFactor"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int NestNumber { + get { + return ((int)(this["NestNumber"])); + } + set { + this["NestNumber"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("200")] + public int ImportSplinePrecision { + get { + return ((int)(this["ImportSplinePrecision"])); + } + set { + this["ImportSplinePrecision"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Inches")] + public global::OpenNest.Units DefaultUnit { + get { + return ((global::OpenNest.Units)(this["DefaultUnit"])); + } + set { + this["DefaultUnit"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("275")] + public int SplitterDistance { + get { + return ((int)(this["SplitterDistance"])); + } + set { + this["SplitterDistance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("04/08/2015 19:30:00")] + public global::System.DateTime LastNestCreatedDate { + get { + return ((global::System.DateTime)(this["LastNestCreatedDate"])); + } + set { + this["LastNestCreatedDate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string NestTemplatePath { + get { + return ((string)(this["NestTemplatePath"])); + } + set { + this["NestTemplatePath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public decimal LastFeedrate { + get { + return ((decimal)(this["LastFeedrate"])); + } + set { + this["LastFeedrate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public decimal LastRapidFeedrate { + get { + return ((decimal)(this["LastRapidFeedrate"])); + } + set { + this["LastRapidFeedrate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public decimal LastPierceTime { + get { + return ((decimal)(this["LastPierceTime"])); + } + set { + this["LastPierceTime"] = value; + } + } + } +} diff --git a/Source/OpenNest/Properties/Settings.settings b/Source/OpenNest/Properties/Settings.settings new file mode 100644 index 0000000..a699740 --- /dev/null +++ b/Source/OpenNest/Properties/Settings.settings @@ -0,0 +1,54 @@ + + + + + + 0, 0 + + + 0, 0 + + + Normal + + + False + + + True + + + True + + + 0.5 + + + 1 + + + 200 + + + Inches + + + 275 + + + 04/08/2015 19:30:00 + + + + + + 1 + + + 1 + + + 0 + + + \ No newline at end of file diff --git a/Source/OpenNest/PushDirection.cs b/Source/OpenNest/PushDirection.cs new file mode 100644 index 0000000..eb01c5c --- /dev/null +++ b/Source/OpenNest/PushDirection.cs @@ -0,0 +1,11 @@ + +namespace OpenNest +{ + public enum PushDirection + { + Up, + Down, + Left, + Right + } +} diff --git a/Source/OpenNest/Resources/add.png b/Source/OpenNest/Resources/add.png new file mode 100644 index 0000000..9cb4036 Binary files /dev/null and b/Source/OpenNest/Resources/add.png differ diff --git a/Source/OpenNest/Resources/clear.png b/Source/OpenNest/Resources/clear.png new file mode 100644 index 0000000..406bad4 Binary files /dev/null and b/Source/OpenNest/Resources/clear.png differ diff --git a/Source/OpenNest/Resources/clock.png b/Source/OpenNest/Resources/clock.png new file mode 100644 index 0000000..4d05242 Binary files /dev/null and b/Source/OpenNest/Resources/clock.png differ diff --git a/Source/OpenNest/Resources/doc_new.png b/Source/OpenNest/Resources/doc_new.png new file mode 100644 index 0000000..4472689 Binary files /dev/null and b/Source/OpenNest/Resources/doc_new.png differ diff --git a/Source/OpenNest/Resources/doc_open.png b/Source/OpenNest/Resources/doc_open.png new file mode 100644 index 0000000..a8e4496 Binary files /dev/null and b/Source/OpenNest/Resources/doc_open.png differ diff --git a/Source/OpenNest/Resources/edit_icon.ico b/Source/OpenNest/Resources/edit_icon.ico new file mode 100644 index 0000000..313afe0 Binary files /dev/null and b/Source/OpenNest/Resources/edit_icon.ico differ diff --git a/Source/OpenNest/Resources/import.png b/Source/OpenNest/Resources/import.png new file mode 100644 index 0000000..bc85d0d Binary files /dev/null and b/Source/OpenNest/Resources/import.png differ diff --git a/Source/OpenNest/Resources/move_first.png b/Source/OpenNest/Resources/move_first.png new file mode 100644 index 0000000..4840fbd Binary files /dev/null and b/Source/OpenNest/Resources/move_first.png differ diff --git a/Source/OpenNest/Resources/move_last.png b/Source/OpenNest/Resources/move_last.png new file mode 100644 index 0000000..fbf850c Binary files /dev/null and b/Source/OpenNest/Resources/move_last.png differ diff --git a/Source/OpenNest/Resources/move_next.png b/Source/OpenNest/Resources/move_next.png new file mode 100644 index 0000000..f997a0b Binary files /dev/null and b/Source/OpenNest/Resources/move_next.png differ diff --git a/Source/OpenNest/Resources/move_previous.png b/Source/OpenNest/Resources/move_previous.png new file mode 100644 index 0000000..bc2773e Binary files /dev/null and b/Source/OpenNest/Resources/move_previous.png differ diff --git a/Source/OpenNest/Resources/remove.png b/Source/OpenNest/Resources/remove.png new file mode 100644 index 0000000..5cc5f76 Binary files /dev/null and b/Source/OpenNest/Resources/remove.png differ diff --git a/Source/OpenNest/Resources/rotate_ccw.png b/Source/OpenNest/Resources/rotate_ccw.png new file mode 100644 index 0000000..0cc9982 Binary files /dev/null and b/Source/OpenNest/Resources/rotate_ccw.png differ diff --git a/Source/OpenNest/Resources/rotate_cw.png b/Source/OpenNest/Resources/rotate_cw.png new file mode 100644 index 0000000..0e1bccd Binary files /dev/null and b/Source/OpenNest/Resources/rotate_cw.png differ diff --git a/Source/OpenNest/Resources/save.png b/Source/OpenNest/Resources/save.png new file mode 100644 index 0000000..988b9a8 Binary files /dev/null and b/Source/OpenNest/Resources/save.png differ diff --git a/Source/OpenNest/Resources/save_as.png b/Source/OpenNest/Resources/save_as.png new file mode 100644 index 0000000..f0302b4 Binary files /dev/null and b/Source/OpenNest/Resources/save_as.png differ diff --git a/Source/OpenNest/Resources/watermark.png b/Source/OpenNest/Resources/watermark.png new file mode 100644 index 0000000..9f0f2b7 Binary files /dev/null and b/Source/OpenNest/Resources/watermark.png differ diff --git a/Source/OpenNest/Resources/zoom_all.png b/Source/OpenNest/Resources/zoom_all.png new file mode 100644 index 0000000..56c7e31 Binary files /dev/null and b/Source/OpenNest/Resources/zoom_all.png differ diff --git a/Source/OpenNest/Resources/zoom_in.png b/Source/OpenNest/Resources/zoom_in.png new file mode 100644 index 0000000..a68d30a Binary files /dev/null and b/Source/OpenNest/Resources/zoom_in.png differ diff --git a/Source/OpenNest/Resources/zoom_out.png b/Source/OpenNest/Resources/zoom_out.png new file mode 100644 index 0000000..d73eaff Binary files /dev/null and b/Source/OpenNest/Resources/zoom_out.png differ diff --git a/Source/OpenNest/SelectionType.cs b/Source/OpenNest/SelectionType.cs new file mode 100644 index 0000000..fc172f2 --- /dev/null +++ b/Source/OpenNest/SelectionType.cs @@ -0,0 +1,8 @@ +namespace OpenNest +{ + public enum SelectionType + { + Intersect, + Contains + } +} diff --git a/Source/OpenNest/ToolStripRenderer.cs b/Source/OpenNest/ToolStripRenderer.cs new file mode 100644 index 0000000..3158aee --- /dev/null +++ b/Source/OpenNest/ToolStripRenderer.cs @@ -0,0 +1,534 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +namespace OpenNest +{ + public enum ToolbarTheme + { + Toolbar, + MediaToolbar, + CommunicationsToolbar, + BrowserTabBar, + HelpBar + } + + /// Renders a toolstrip using the UxTheme API via VisualStyleRenderer and a specific style. + /// Perhaps surprisingly, this does not need to be disposable. + public class ToolStripRenderer : ToolStripSystemRenderer + { + VisualStyleRenderer renderer; + + public ToolStripRenderer(ToolbarTheme theme) + { + Theme = theme; + } + + /// + /// It shouldn't be necessary to P/Invoke like this, however VisualStyleRenderer.GetMargins + /// misses out a parameter in its own P/Invoke. + /// + static internal class NativeMethods + { + [StructLayout(LayoutKind.Sequential)] + public struct MARGINS + { + public int cxLeftWidth; + public int cxRightWidth; + public int cyTopHeight; + public int cyBottomHeight; + } + + [DllImport("uxtheme.dll")] + public extern static int GetThemeMargins(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, int iPropId, IntPtr rect, out MARGINS pMargins); + } + + // See http://msdn2.microsoft.com/en-us/library/bb773210.aspx - "Parts and States" + // Only menu-related parts/states are needed here, VisualStyleRenderer handles most of the rest. + enum MenuParts : int + { + ItemTMSchema = 1, + DropDownTMSchema = 2, + BarItemTMSchema = 3, + BarDropDownTMSchema = 4, + ChevronTMSchema = 5, + SeparatorTMSchema = 6, + BarBackground = 7, + BarItem = 8, + PopupBackground = 9, + PopupBorders = 10, + PopupCheck = 11, + PopupCheckBackground = 12, + PopupGutter = 13, + PopupItem = 14, + PopupSeparator = 15, + PopupSubmenu = 16, + SystemClose = 17, + SystemMaximize = 18, + SystemMinimize = 19, + SystemRestore = 20 + } + + enum MenuBarStates : int + { + Active = 1, + Inactive = 2 + } + + enum MenuBarItemStates : int + { + Normal = 1, + Hover = 2, + Pushed = 3, + Disabled = 4, + DisabledHover = 5, + DisabledPushed = 6 + } + + enum MenuPopupItemStates : int + { + Normal = 1, + Hover = 2, + Disabled = 3, + DisabledHover = 4 + } + + enum MenuPopupCheckStates : int + { + CheckmarkNormal = 1, + CheckmarkDisabled = 2, + BulletNormal = 3, + BulletDisabled = 4 + } + + enum MenuPopupCheckBackgroundStates : int + { + Disabled = 1, + Normal = 2, + Bitmap = 3 + } + + enum MenuPopupSubMenuStates : int + { + Normal = 1, + Disabled = 2 + } + + enum MarginTypes : int + { + Sizing = 3601, + Content = 3602, + Caption = 3603 + } + + static readonly int RebarBackground = 6; + + Padding GetThemeMargins(IDeviceContext dc, MarginTypes marginType) + { + NativeMethods.MARGINS margins; + try + { + IntPtr hDC = dc.GetHdc(); + if (0 == NativeMethods.GetThemeMargins(renderer.Handle, hDC, renderer.Part, renderer.State, (int)marginType, IntPtr.Zero, out margins)) + return new Padding(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + return new Padding(0); + } + finally + { + dc.ReleaseHdc(); + } + } + + private static int GetItemState(ToolStripItem item) + { + bool hot = item.Selected; + + if (item.IsOnDropDown) + { + if (item.Enabled) + return hot ? (int)MenuPopupItemStates.Hover : (int)MenuPopupItemStates.Normal; + return hot ? (int)MenuPopupItemStates.DisabledHover : (int)MenuPopupItemStates.Disabled; + } + else { + if (item.Pressed) + return item.Enabled ? (int)MenuBarItemStates.Pushed : (int)MenuBarItemStates.DisabledPushed; + if (item.Enabled) + return hot ? (int)MenuBarItemStates.Hover : (int)MenuBarItemStates.Normal; + return hot ? (int)MenuBarItemStates.DisabledHover : (int)MenuBarItemStates.Disabled; + } + } + + public ToolbarTheme Theme + { + get; + set; + } + + private string RebarClass + { + get + { + return SubclassPrefix + "Rebar"; + } + } + + private string ToolbarClass + { + get + { + return SubclassPrefix + "ToolBar"; + } + } + + private string MenuClass + { + get + { + return SubclassPrefix + "Menu"; + } + } + + private string SubclassPrefix + { + get + { + switch (Theme) + { + case ToolbarTheme.MediaToolbar: return "Media::"; + case ToolbarTheme.CommunicationsToolbar: return "Communications::"; + case ToolbarTheme.BrowserTabBar: return "BrowserTabBar::"; + case ToolbarTheme.HelpBar: return "Help::"; + default: return string.Empty; + } + } + } + + private VisualStyleElement Subclass(VisualStyleElement element) + { + return VisualStyleElement.CreateElement(SubclassPrefix + element.ClassName, + element.Part, element.State); + } + + private bool EnsureRenderer() + { + if (!IsSupported) + return false; + + if (renderer == null) + renderer = new VisualStyleRenderer(VisualStyleElement.Button.PushButton.Normal); + + return true; + } + + // Gives parented ToolStrips a transparent background. + protected override void Initialize(ToolStrip toolStrip) + { + if (toolStrip.Parent is ToolStripPanel) + toolStrip.BackColor = Color.Transparent; + + base.Initialize(toolStrip); + } + + // Using just ToolStripManager.Renderer without setting the Renderer individually per ToolStrip means + // that the ToolStrip is not passed to the Initialize method. ToolStripPanels, however, are. So we can + // simply initialize it here too, and this should guarantee that the ToolStrip is initialized at least + // once. Hopefully it isn't any more complicated than this. + protected override void InitializePanel(ToolStripPanel toolStripPanel) + { + foreach (Control control in toolStripPanel.Controls) + if (control is ToolStrip) + Initialize((ToolStrip)control); + + base.InitializePanel(toolStripPanel); + } + + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + if (EnsureRenderer()) + { + renderer.SetParameters(MenuClass, (int)MenuParts.PopupBorders, 0); + if (e.ToolStrip.IsDropDown) + { + Region oldClip = e.Graphics.Clip; + + // Tool strip borders are rendered *after* the content, for some reason. + // So we have to exclude the inside of the popup otherwise we'll draw over it. + Rectangle insideRect = e.ToolStrip.ClientRectangle; + insideRect.Inflate(-1, -1); + e.Graphics.ExcludeClip(insideRect); + + renderer.DrawBackground(e.Graphics, e.ToolStrip.ClientRectangle, e.AffectedBounds); + + // Restore the old clip in case the Graphics is used again (does that ever happen?) + e.Graphics.Clip = oldClip; + } + } + else { + base.OnRenderToolStripBorder(e); + } + } + + Rectangle GetBackgroundRectangle(ToolStripItem item) + { + if (!item.IsOnDropDown) + return new Rectangle(new Point(), item.Bounds.Size); + + // For a drop-down menu item, the background rectangles of the items should be touching vertically. + // This ensures that's the case. + Rectangle rect = item.Bounds; + + // The background rectangle should be inset two pixels horizontally (on both sides), but we have + // to take into account the border. + rect.X = item.ContentRectangle.X + 1; + rect.Width = item.ContentRectangle.Width - 1; + + // Make sure we're using all of the vertical space, so that the edges touch. + rect.Y = 0; + return rect; + } + + protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) + { + if (EnsureRenderer()) + { + int partID = e.Item.IsOnDropDown ? (int)MenuParts.PopupItem : (int)MenuParts.BarItem; + renderer.SetParameters(MenuClass, partID, GetItemState(e.Item)); + + Rectangle bgRect = GetBackgroundRectangle(e.Item); + renderer.DrawBackground(e.Graphics, bgRect, bgRect); + } + else { + base.OnRenderMenuItemBackground(e); + } + } + + protected override void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e) + { + if (EnsureRenderer()) + { + // Draw the background using Rebar & RP_BACKGROUND (or, if that is not available, fall back to + // Rebar.Band.Normal) + if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.CreateElement(RebarClass, RebarBackground, 0))) + { + renderer.SetParameters(RebarClass, RebarBackground, 0); + } + else { + renderer.SetParameters(RebarClass, 0, 0); + } + + if (renderer.IsBackgroundPartiallyTransparent()) + renderer.DrawParentBackground(e.Graphics, e.ToolStripPanel.ClientRectangle, e.ToolStripPanel); + + renderer.DrawBackground(e.Graphics, e.ToolStripPanel.ClientRectangle); + + e.Handled = true; + } + else { + base.OnRenderToolStripPanelBackground(e); + } + } + + // Render the background of an actual menu bar, dropdown menu or toolbar. + protected override void OnRenderToolStripBackground(System.Windows.Forms.ToolStripRenderEventArgs e) + { + if (EnsureRenderer()) + { + if (e.ToolStrip.IsDropDown) + { + renderer.SetParameters(MenuClass, (int)MenuParts.PopupBackground, 0); + } + else { + // It's a MenuStrip or a ToolStrip. If it's contained inside a larger panel, it should have a + // transparent background, showing the panel's background. + + if (e.ToolStrip.Parent is ToolStripPanel) + { + // The background should be transparent, because the ToolStripPanel's background will be visible. + // (Of course, we assume the ToolStripPanel is drawn using the same theme, but it's not my fault + // if someone does that.) + return; + } + else { + // A lone toolbar/menubar should act like it's inside a toolbox, I guess. + // Maybe I should use the MenuClass in the case of a MenuStrip, although that would break + // the other themes... + if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.CreateElement(RebarClass, RebarBackground, 0))) + renderer.SetParameters(RebarClass, RebarBackground, 0); + else + renderer.SetParameters(RebarClass, 0, 0); + } + } + + if (renderer.IsBackgroundPartiallyTransparent()) + renderer.DrawParentBackground(e.Graphics, e.ToolStrip.ClientRectangle, e.ToolStrip); + + renderer.DrawBackground(e.Graphics, e.ToolStrip.ClientRectangle, e.AffectedBounds); + } + else { + base.OnRenderToolStripBackground(e); + } + } + + // The only purpose of this override is to change the arrow colour. + // It's OK to just draw over the default arrow since we also pass down arrow drawing to the system renderer. + protected override void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e) + { + if (EnsureRenderer()) + { + ToolStripSplitButton sb = (ToolStripSplitButton)e.Item; + base.OnRenderSplitButtonBackground(e); + + // It doesn't matter what colour of arrow we tell it to draw. OnRenderArrow will compute it from the item anyway. + OnRenderArrow(new ToolStripArrowRenderEventArgs(e.Graphics, sb, sb.DropDownButtonBounds, Color.Red, ArrowDirection.Down)); + } + else { + base.OnRenderSplitButtonBackground(e); + } + } + + Color GetItemTextColor(ToolStripItem item) + { + int partId = item.IsOnDropDown ? (int)MenuParts.PopupItem : (int)MenuParts.BarItem; + renderer.SetParameters(MenuClass, partId, GetItemState(item)); + return renderer.GetColor(ColorProperty.TextColor); + } + + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) + { + if (EnsureRenderer()) + e.TextColor = GetItemTextColor(e.Item); + + base.OnRenderItemText(e); + } + + protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) + { + if (EnsureRenderer()) + { + if (e.ToolStrip.IsDropDown) + { + renderer.SetParameters(MenuClass, (int)MenuParts.PopupGutter, 0); + // The AffectedBounds is usually too small, way too small to look right. Instead of using that, + // use the AffectedBounds but with the right width. Then narrow the rectangle to the correct edge + // based on whether or not it's RTL. (It doesn't need to be narrowed to an edge in LTR mode, but let's + // do that anyway.) + // Using the DisplayRectangle gets roughly the right size so that the separator is closer to the text. + Padding margins = GetThemeMargins(e.Graphics, MarginTypes.Sizing); + int extraWidth = (e.ToolStrip.Width - e.ToolStrip.DisplayRectangle.Width - margins.Left - margins.Right - 1) - e.AffectedBounds.Width; + Rectangle rect = e.AffectedBounds; + rect.Y += 2; + rect.Height -= 4; + int sepWidth = renderer.GetPartSize(e.Graphics, ThemeSizeType.True).Width; + if (e.ToolStrip.RightToLeft == RightToLeft.Yes) + { + rect = new Rectangle(rect.X - extraWidth, rect.Y, sepWidth, rect.Height); + rect.X += sepWidth; + } + else { + rect = new Rectangle(rect.Width + extraWidth - sepWidth, rect.Y, sepWidth, rect.Height); + } + renderer.DrawBackground(e.Graphics, rect); + } + } + else { + base.OnRenderImageMargin(e); + } + } + + protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) + { + if (e.ToolStrip.IsDropDown && EnsureRenderer()) + { + renderer.SetParameters(MenuClass, (int)MenuParts.PopupSeparator, 0); + Rectangle rect = new Rectangle(e.ToolStrip.DisplayRectangle.Left, 0, e.ToolStrip.DisplayRectangle.Width, e.Item.Height); + renderer.DrawBackground(e.Graphics, rect, rect); + } + else + { + e.Graphics.DrawLine(Pens.LightGray, + e.Item.ContentRectangle.X, + e.Item.ContentRectangle.Y, + e.Item.ContentRectangle.X, + e.Item.ContentRectangle.Y + e.Item.Height - 6); + } + } + + protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) + { + if (EnsureRenderer()) + { + Rectangle bgRect = GetBackgroundRectangle(e.Item); + bgRect.Width = bgRect.Height; + + // Now, mirror its position if the menu item is RTL. + if (e.Item.RightToLeft == RightToLeft.Yes) + bgRect = new Rectangle(e.ToolStrip.ClientSize.Width - bgRect.X - bgRect.Width, bgRect.Y, bgRect.Width, bgRect.Height); + + renderer.SetParameters(MenuClass, (int)MenuParts.PopupCheckBackground, e.Item.Enabled ? (int)MenuPopupCheckBackgroundStates.Normal : (int)MenuPopupCheckBackgroundStates.Disabled); + renderer.DrawBackground(e.Graphics, bgRect); + + Rectangle checkRect = e.ImageRectangle; + checkRect.X = bgRect.X + bgRect.Width / 2 - checkRect.Width / 2; + checkRect.Y = bgRect.Y + bgRect.Height / 2 - checkRect.Height / 2; + + // I don't think ToolStrip even supports radio box items, so no need to render them. + renderer.SetParameters(MenuClass, (int)MenuParts.PopupCheck, e.Item.Enabled ? (int)MenuPopupCheckStates.CheckmarkNormal : (int)MenuPopupCheckStates.CheckmarkDisabled); + + renderer.DrawBackground(e.Graphics, checkRect); + } + else { + base.OnRenderItemCheck(e); + } + } + + protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) + { + // The default renderer will draw an arrow for us (the UXTheme API seems not to have one for all directions), + // but it will get the colour wrong in many cases. The text colour is probably the best colour to use. + if (EnsureRenderer()) + e.ArrowColor = GetItemTextColor(e.Item); + base.OnRenderArrow(e); + } + + protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) + { + if (EnsureRenderer()) + { + // BrowserTabBar::Rebar draws the chevron using the default background. Odd. + string rebarClass = RebarClass; + if (Theme == ToolbarTheme.BrowserTabBar) + rebarClass = "Rebar"; + + int state = VisualStyleElement.Rebar.Chevron.Normal.State; + if (e.Item.Pressed) + state = VisualStyleElement.Rebar.Chevron.Pressed.State; + else if (e.Item.Selected) + state = VisualStyleElement.Rebar.Chevron.Hot.State; + + renderer.SetParameters(rebarClass, VisualStyleElement.Rebar.Chevron.Normal.Part, state); + renderer.DrawBackground(e.Graphics, new Rectangle(Point.Empty, e.Item.Size)); + } + else { + base.OnRenderOverflowButtonBackground(e); + } + } + + public bool IsSupported + { + get + { + if (!VisualStyleRenderer.IsSupported) + return false; + + // Needs a more robust check. It seems mono supports very different style sets. + return + VisualStyleRenderer.IsElementDefined( + VisualStyleElement.CreateElement("Menu", + (int)MenuParts.BarBackground, + (int)MenuBarStates.Active)); + } + } + } +} diff --git a/Source/OpenNest/Win32.cs b/Source/OpenNest/Win32.cs new file mode 100644 index 0000000..990e639 --- /dev/null +++ b/Source/OpenNest/Win32.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace OpenNest +{ + public static class Win32 + { + [DllImport("kernel32")] + public static extern long WritePrivateProfileString( + string section, + string key, + string val, + string filePath); + + [DllImport("kernel32")] + public static extern int GetPrivateProfileString( + string section, + string key, + string def, + StringBuilder retVal, + int size, + string filePath); + + [DllImport("user32.dll")] + public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", ExactSpelling = true)] + public static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + #region Constants + + public const int GWL_EXSTYLE = -20; + public const int WS_EX_CLIENTEDGE = 0x200; + public const uint SWP_NOSIZE = 0x0001; + public const uint SWP_NOMOVE = 0x0002; + public const uint SWP_NOZORDER = 0x0004; + public const uint SWP_NOREDRAW = 0x0008; + public const uint SWP_NOACTIVATE = 0x0010; + public const uint SWP_FRAMECHANGED = 0x0020; + public const uint SWP_SHOWWINDOW = 0x0040; + public const uint SWP_HIDEWINDOW = 0x0080; + public const uint SWP_NOCOPYBITS = 0x0100; + public const uint SWP_NOOWNERZORDER = 0x0200; + public const uint SWP_NOSENDCHANGING = 0x0400; + + #endregion Constants + } +} diff --git a/Source/OpenNest/app.config b/Source/OpenNest/app.config new file mode 100644 index 0000000..24d263e --- /dev/null +++ b/Source/OpenNest/app.config @@ -0,0 +1,69 @@ + + + + +
+
+ + + + + + 0, 0 + + + 0, 0 + + + Normal + + + False + + + True + + + True + + + 0.5 + + + 1 + + + 200 + + + Inches + + + 275 + + + 04/08/2015 19:30:00 + + + + + + 1 + + + 1 + + + 0 + + + + + 0, 0 + + + 0, 0 + + + +