From 4111a4651e9b7fe32063be4ddf4321710b0f6b91 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Mon, 26 Mar 2018 16:54:36 -0700 Subject: [PATCH 1/8] Move editors into ApplicationController - Issue MatterHackers/MCCentral#2985 Public properties editor should use existing editor system --- ApplicationView/ApplicationController.cs | 64 +++++++++++++++++++----- PartPreviewWindow/SelectedObjectPanel.cs | 49 ++---------------- 2 files changed, 55 insertions(+), 58 deletions(-) diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index f4f4ca66e..59f6f78ae 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -73,7 +73,7 @@ namespace MatterHackers.MatterControl public class AppContext { /// - /// Native platform features + /// Native platform features /// public static INativePlatformFeatures Platform { get; set; } @@ -87,6 +87,8 @@ namespace MatterHackers.MatterControl public class ApplicationController { + private Dictionary> objectEditorsByType; + public ThemeConfig Theme { get; set; } = new ThemeConfig(); public RunningTasksConfig Tasks { get; set; } = new RunningTasksConfig(); @@ -98,7 +100,7 @@ namespace MatterHackers.MatterControl private static string cacheDirectory = Path.Combine(ApplicationDataStorage.ApplicationUserDataPath, "data", "temp", "cache"); - // TODO: Any references to this property almost certainly need to be reconsidered. ActiveSliceSettings static references that assume a single printer + // TODO: Any references to this property almost certainly need to be reconsidered. ActiveSliceSettings static references that assume a single printer // selection are being redirected here. This allows us to break the dependency to the original statics and consolidates // us down to a single point where code is making assumptions about the presence of a printer, printer counts, etc. If we previously checked for // PrinterConnection.IsPrinterConnected, that could should be updated to iterate ActiverPrinters, checking each one and acting on each as it would @@ -164,7 +166,7 @@ namespace MatterHackers.MatterControl } BedSettings.SetMakeAndModel( - printer.Settings.GetValue(SettingsKey.make), + printer.Settings.GetValue(SettingsKey.make), printer.Settings.GetValue(SettingsKey.model)); ActiveSliceSettings.SwitchToPrinterTheme(); @@ -275,7 +277,7 @@ namespace MatterHackers.MatterControl } else { - // Process until queuedThumbCallbacks is empty then wait for new tasks via QueueForGeneration + // Process until queuedThumbCallbacks is empty then wait for new tasks via QueueForGeneration thumbGenResetEvent.WaitOne(); } } @@ -470,7 +472,7 @@ namespace MatterHackers.MatterControl fit.MakeNameNonColliding(); scene.UndoBuffer.AddAndDo(new ReplaceCommand(new List { selectedItem }, new List { fit })); - + scene.SelectedItem = fit; }, //Icon = AggContext.StaticData.LoadIcon("array_linear.png").SetPreMultiply(), @@ -696,7 +698,7 @@ namespace MatterHackers.MatterControl { Tasks.Execute("Disable Heaters".Localize(), (reporter, cancellationToken) => { - EventHandler heatChanged = (s2, e2) => + EventHandler heatChanged = (s2, e2) => { printerConnection.ContinuWaitingToTurnOffHeaters = false; }; @@ -744,7 +746,6 @@ namespace MatterHackers.MatterControl } }, ref unregisterEvents); - PrinterConnection.ErrorReported.RegisterEvent((s, e) => { var foundStringEventArgs = e as FoundStringEventArgs; @@ -778,6 +779,45 @@ namespace MatterHackers.MatterControl } } }, ref unregisterEvents); + + HashSet mappedEditors; + objectEditorsByType = new Dictionary>(); + + foreach (IObject3DEditor editor in PluginFinder.CreateInstancesOf()) + { + foreach (Type type in editor.SupportedTypes()) + { + if (!objectEditorsByType.TryGetValue(type, out mappedEditors)) + { + mappedEditors = new HashSet(); + objectEditorsByType.Add(type, mappedEditors); + } + + mappedEditors.Add(editor); + } + } + } + + public HashSet GetEditorsForType(Type selectedItemType) + { + HashSet mappedEditors; + objectEditorsByType.TryGetValue(selectedItemType, out mappedEditors); + + if (mappedEditors == null) + { + foreach (var kvp in objectEditorsByType) + { + var editorType = kvp.Key; + + if (editorType.IsAssignableFrom(selectedItemType)) + { + mappedEditors = kvp.Value; + break; + } + } + } + + return mappedEditors; } internal void Shutdown() @@ -915,7 +955,7 @@ namespace MatterHackers.MatterControl string extensionWithoutPeriod = extension.Trim('.'); return !string.IsNullOrEmpty(extension) - && (ApplicationSettings.OpenDesignFileParams.Contains(extension) + && (ApplicationSettings.OpenDesignFileParams.Contains(extension) || this.Library.ContentProviders.Keys.Contains(extensionWithoutPeriod)); } @@ -1095,7 +1135,7 @@ namespace MatterHackers.MatterControl if (AssetObject3D.AssetManager == null) { - AssetObject3D.AssetManager = new AssetManager(); + AssetObject3D.AssetManager = new AssetManager(); } //HtmlWindowTest(); @@ -1151,7 +1191,7 @@ namespace MatterHackers.MatterControl // If profiles.json was created, run the import wizard to pull in any SQLite printers if (guest?.Profiles?.Any() == true - && !ProfileManager.Instance.IsGuestProfile + && !ProfileManager.Instance.IsGuestProfile && !ProfileManager.Instance.PrintersImported) { // Show the import printers wizard @@ -1575,8 +1615,8 @@ namespace MatterHackers.MatterControl await ApplicationController.Instance.Tasks.Execute("Slicing".Localize(), async (reporter, cancellationToken) => { slicingSucceeded = await Slicer.SliceItem( - object3D, - gcodeFilePath, + object3D, + gcodeFilePath, printer, new SliceProgressReporter(reporter, printer), cancellationToken); diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index 086dc92ee..aa6ee0f46 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -61,7 +61,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private View3DWidget view3DWidget; private InteractiveScene scene; private PrinterConfig printer; - private Dictionary> objectEditorsByType; private SectionWidget editorSection; private TextButton editButton; @@ -114,7 +113,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Margin = new BorderDouble(0) }); - var editorColumn = new FlowLayoutWidget(FlowDirection.TopToBottom) { HAnchor = HAnchor.Stretch, @@ -248,25 +246,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { sectionWidget.ContentPanel.Padding = new BorderDouble(10, 10, 10, 0); } - - HashSet mappedEditors; - objectEditorsByType = new Dictionary>(); - - // TODO: Consider only loading once into a static - var objectEditors = PluginFinder.CreateInstancesOf(); - foreach (IObject3DEditor editor in objectEditors) - { - foreach (Type type in editor.SupportedTypes()) - { - if (!objectEditorsByType.TryGetValue(type, out mappedEditors)) - { - mappedEditors = new HashSet(); - objectEditorsByType.Add(type, mappedEditors); - } - - mappedEditors.Add(editor); - } - } } private static Type componentAttribute = typeof(IObject3DComponentAttribute); @@ -293,7 +272,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.Parent.Visible = viewMode == null || viewMode == PartViewMode.Model; - HashSet mappedEditors = GetEditorsForType(selectedItemType); + HashSet mappedEditors = ApplicationController.Instance.GetEditorsForType(selectedItemType); var activeEditors = new List<(IObject3DEditor, IObject3D)>(); @@ -313,7 +292,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Shown known editors for any matching properties foreach (var member in members) { - if (this.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) + if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) { activeEditors.Add((editor, member.Value)); } @@ -334,7 +313,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Shown known editors for any matching properties foreach (var member in members) { - if (this.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) + if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) { activeEditors.Add((editor, member.Value)); } @@ -350,28 +329,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow ShowObjectEditor(activeEditors); } - private HashSet GetEditorsForType(Type selectedItemType) - { - HashSet mappedEditors; - objectEditorsByType.TryGetValue(selectedItemType, out mappedEditors); - - if (mappedEditors == null) - { - foreach (var kvp in objectEditorsByType) - { - var editorType = kvp.Key; - - if (editorType.IsAssignableFrom(selectedItemType)) - { - mappedEditors = kvp.Value; - break; - } - } - } - - return mappedEditors; - } - private class OperationButton :TextButton { private NodeOperation graphOperation; From 03b9d21594643d33ed3e56b31df3de01e85d9839 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 12:24:46 -0700 Subject: [PATCH 2/8] Migrate Lithophane into base --- DesignTools/Lithophane.cs | 194 ++++++++++++++++++ DesignTools/LithophaneObject3D.cs | 91 ++++++++ MatterControl.csproj | 2 + .../View3D/Actions/ImageEditor.cs | 6 +- 4 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 DesignTools/Lithophane.cs create mode 100644 DesignTools/LithophaneObject3D.cs diff --git a/DesignTools/Lithophane.cs b/DesignTools/Lithophane.cs new file mode 100644 index 000000000..cc2f6e885 --- /dev/null +++ b/DesignTools/Lithophane.cs @@ -0,0 +1,194 @@ +/* +Copyright (c) 2018, John Lewin + */ + +using System; +using System.Diagnostics; +using System.Linq; +using MatterHackers.Agg; +using MatterHackers.Agg.Image; +using MatterHackers.PolygonMesh; +using MatterHackers.VectorMath; + +namespace MatterHackers.MatterControl.Plugins.Lithophane +{ + public static class Lithophane + { + class PixelInfo + { + public IVertex Top { get; set; } + public IVertex Bottom { get; set; } + } + + public static Mesh Generate(IImageData resizedImage, double maxZ, double nozzleWidth, double pixelsPerMM, bool invert, IProgress reporter) + { + // TODO: Move this to a user supplied value + double baseThickness = nozzleWidth; // base thickness (in mm) + double zRange = maxZ - baseThickness; + + // Dimensions of image + var width = resizedImage.Width; + var height = resizedImage.Height; + + var zScale = zRange / 255; + + var pixelData = resizedImage.Pixels; + + Stopwatch stopwatch = Stopwatch.StartNew(); + + var mesh = new Mesh(); + + //var rescale = (double)onPlateWidth / imageData.Width; + var rescale = 1; + + var progressStatus = new ProgressStatus(); + + // Build an array of PixelInfo objects from each pixel + // Collapse from 4 bytes per pixel to one - makes subsequent processing more logical and has minimal cost + var pixels = pixelData.Where((x, i) => i % 4 == 0) + + // Interpolate the pixel color to zheight + .Select(b => baseThickness + (invert ? 255 - b : b) * zScale) + + // Create a Vector3 for each pixel at the computed x/y/z + .Select((z, i) => mesh.CreateVertex(new Vector3( + i % width * rescale, + (i - i % width) / width * rescale * -1, + z))) + + // Create a mirrored vector for the pixel at z0 and return with top/bottom paired together + .Select(vec => new PixelInfo() + { + Top = vec, + Bottom = mesh.CreateVertex(new Vector3( + vec.Position.X, + vec.Position.Y, + 0)) + }).ToArray(); + + Console.WriteLine("ElapsedTime - PixelInfo Linq Generation: {0}", stopwatch.ElapsedMilliseconds); + stopwatch.Restart(); + + // Select pixels along image edges + var backRow = pixels.Take(width).Reverse().ToArray(); + var frontRow = pixels.Skip((height - 1) * width).Take(width).ToArray(); + var leftRow = pixels.Where((x, i) => i % width == 0).ToArray(); + var rightRow = pixels.Where((x, i) => (i + 1) % width == 0).Reverse().ToArray(); + + int k, + nextJ, + nextK; + + var notificationInterval = 100; + + var workCount = (resizedImage.Width - 1) * (resizedImage.Height - 1) + + (height - 1) + + (width - 1); + + double workIndex = 0; + + // Vertical faces: process each row and column, creating the top and bottom faces as appropriate + for (int i = 0; i < resizedImage.Height - 1; ++i) + { + var startAt = i * width; + + // Process each column + for (int j = startAt; j < startAt + resizedImage.Width - 1; ++j) + { + k = j + 1; + nextJ = j + resizedImage.Width; + nextK = nextJ + 1; + + // Create north, then south face + mesh.CreateFace(new IVertex[] { pixels[k].Top, pixels[j].Top, pixels[nextJ].Top, pixels[nextK].Top }); + mesh.CreateFace(new IVertex[] { pixels[j].Bottom, pixels[k].Bottom, pixels[nextK].Bottom, pixels[nextJ].Bottom }); + workIndex++; + + if (workIndex % notificationInterval == 0) + { + progressStatus.Progress0To1 = workIndex / workCount; + reporter.Report(progressStatus); + } + } + } + + // Side faces: East/West + for (int j = 0; j < height - 1; ++j) + { + //Next row + k = j + 1; + + // Create east, then west face + mesh.CreateFace(new IVertex[] { leftRow[k].Top, leftRow[j].Top, leftRow[j].Bottom, leftRow[k].Bottom }); + mesh.CreateFace(new IVertex[] { rightRow[k].Top, rightRow[j].Top, rightRow[j].Bottom, rightRow[k].Bottom }); + workIndex++; + + if (workIndex % notificationInterval == 0) + { + progressStatus.Progress0To1 = workIndex / workCount; + reporter.Report(progressStatus); + } + } + + // Side faces: North/South + for (int j = 0; j < width - 1; ++j) + { + // Next row + k = j + 1; + + // Create north, then south face + mesh.CreateFace(new IVertex[] { frontRow[k].Top, frontRow[j].Top, frontRow[j].Bottom, frontRow[k].Bottom }); + mesh.CreateFace(new IVertex[] { backRow[k].Top, backRow[j].Top, backRow[j].Bottom, backRow[k].Bottom }); + workIndex++; + + if (workIndex % notificationInterval == 0) + { + progressStatus.Progress0To1 = workIndex / workCount; + reporter.Report(progressStatus); + } + } + + Console.WriteLine("ElapsedTime - Face Generation: {0}", stopwatch.ElapsedMilliseconds); + + return mesh; + } + + public interface IImageData + { + byte[] Pixels { get; } + + int Width { get; } + int Height { get; } + } + + public class ImageBufferImageData : IImageData + { + ImageBuffer resizedImage; + + public ImageBufferImageData(ImageBuffer image, double pixelWidth) + { + resizedImage = this.ToResizedGrayscale(image, pixelWidth); + resizedImage.FlipY(); + } + + public int Width => resizedImage.Width; + public int Height => resizedImage.Height; + + private ImageBuffer ToResizedGrayscale(ImageBuffer image, double onPlateWidth = 0) + { + var ratio = onPlateWidth / image.Width; + + var resizedImage = image.CreateScaledImage(ratio); + + var grayImage = resizedImage.ToGrayscale(); + + // Render grayscale pixels onto resized image with larger pixel format needed by caller + resizedImage.NewGraphics2D().Render(grayImage, 0, 0); + + return resizedImage; + } + + public byte[] Pixels => resizedImage.GetBuffer(); + } + } +} diff --git a/DesignTools/LithophaneObject3D.cs b/DesignTools/LithophaneObject3D.cs new file mode 100644 index 000000000..5df59b971 --- /dev/null +++ b/DesignTools/LithophaneObject3D.cs @@ -0,0 +1,91 @@ +/* +Copyright (c) 2018, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using MatterHackers.Agg.Platform; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.VectorMath; + +namespace MatterHackers.MatterControl.Plugins.Lithophane +{ + public class LithophaneObject3D : Object3D, IRebuildable + { + [IObject3DComponent] + public ImageObject3D Image => this.Children.OfType().FirstOrDefault(); + + [DisplayName("Pixels Per mm"), Range(0.5, 3, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public double PixelsPerMM { get; set; } = 1.5; + + [Range(0.5, 3, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public double Height { get; set; } = 2.5; + + public int Width { get; set; } = 150; + + public bool Invert { get; set; } + + public Vector3 ImageOffset { get; private set; } = Vector3.Zero; + + public void Rebuild(UndoBuffer undoBuffer) + { + var activeImage = AggContext.ImageIO.LoadImage(this.Image.AssetPath); + + ApplicationController.Instance.Tasks.Execute("Generating Lithophane".Localize(), (reporter, cancellationToken) => + { + var generatedMesh = Lithophane.Generate( + new Lithophane.ImageBufferImageData(activeImage, this.Width), + this.Height, + 0.4, + this.PixelsPerMM, + this.Invert, + reporter); + + this.Mesh = generatedMesh; + + // Remove old offset + this.Matrix *= Matrix4X4.CreateTranslation(this.ImageOffset); + + // Set and store new offset + var imageBounds = generatedMesh.GetAxisAlignedBoundingBox(); + this.ImageOffset = imageBounds.Center + new Vector3(0, 0, -imageBounds.Center.Z); + + // Apply offset + this.Matrix *= Matrix4X4.CreateTranslation(-this.ImageOffset); + + return Task.CompletedTask; + }); + } + } +} diff --git a/MatterControl.csproj b/MatterControl.csproj index e8e589a86..14f9e69be 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -94,6 +94,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/PartPreviewWindow/View3D/Actions/ImageEditor.cs b/PartPreviewWindow/View3D/Actions/ImageEditor.cs index d04bdcb29..c3e100f33 100644 --- a/PartPreviewWindow/View3D/Actions/ImageEditor.cs +++ b/PartPreviewWindow/View3D/Actions/ImageEditor.cs @@ -49,6 +49,8 @@ namespace MatterHackers.MatterControl.DesignTools string IObject3DEditor.Name => "Image Editor"; + IEnumerable IObject3DEditor.SupportedTypes() => new[] { typeof(ImageObject3D) }; + public GuiWidget Create(IObject3D item, View3DWidget parentView3D, ThemeConfig theme) { var column = new FlowLayoutWidget(FlowDirection.TopToBottom) @@ -101,7 +103,7 @@ namespace MatterHackers.MatterControl.DesignTools }; } - // add in the invert checkbox and change image button + // add in the invert checkbox and change image button var addButton = new TextButton("Change".Localize(), theme) { BackgroundColor = theme.MinimalShade @@ -188,8 +190,6 @@ namespace MatterHackers.MatterControl.DesignTools return (image.Height <= 185) ? image : ScaleThumbnailImage(185, image); } - IEnumerable IObject3DEditor.SupportedTypes() => new[] { typeof(ImageObject3D) }; - private ImageBuffer ScaleThumbnailImage(int height, ImageBuffer imageBuffer) { if (imageBuffer.Height != height) From 96574587c0d29f3c3d6c89a5b83f627df73c94f0 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 12:25:16 -0700 Subject: [PATCH 3/8] Filter out null properties --- PartPreviewWindow/SelectedObjectPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index aa6ee0f46..fc971d254 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -311,7 +311,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; // Shown known editors for any matching properties - foreach (var member in members) + foreach (var member in members.Where(m => m.Value != null)) { if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) { From b570b5741f4b36305e6005793ada29ffa631c753 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 12:26:52 -0700 Subject: [PATCH 4/8] Use registered editors for public properties - MatterHackers/MCCentral#2985 Public properties editor should use existing editor system --- DesignTools/PublicPropertyEditor.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs index 43c06e7a9..c49dc5448 100644 --- a/DesignTools/PublicPropertyEditor.cs +++ b/DesignTools/PublicPropertyEditor.cs @@ -136,6 +136,8 @@ namespace MatterHackers.MatterControl.DesignTools return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase(); } + private static Type IObject3DType = typeof(IObject3D); + private string GetDescription(PropertyInfo prop) { var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); @@ -457,11 +459,11 @@ namespace MatterHackers.MatterControl.DesignTools theme, undoBuffer); editControlsContainer.AddChild(rowContainer); } - // create an image asset editor - else if (property.Value is ImageObject3D imageObject) + // Use known IObject3D editors + else if (property.Value is IObject3D object3D + && ApplicationController.Instance.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor editor) { - var editor = new ImageEditor(); - rowContainer = editor.Create(imageObject, view3DWidget, theme); + rowContainer = editor.Create( object3D, view3DWidget, theme); editControlsContainer.AddChild(rowContainer); } From 90d2865c8cf0b0033587549327928e1949f663de Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 14:03:12 -0700 Subject: [PATCH 5/8] Revise member order --- DesignTools/PublicPropertyEditor.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs index c49dc5448..a0c02a604 100644 --- a/DesignTools/PublicPropertyEditor.cs +++ b/DesignTools/PublicPropertyEditor.cs @@ -30,7 +30,6 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; using System.ComponentModel; -using System.IO; using System.Linq; using System.Reflection; using MatterHackers.Agg; @@ -41,7 +40,6 @@ using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.DesignTools.Operations; -using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.SlicerConfiguration; using MatterHackers.VectorMath; @@ -57,6 +55,10 @@ namespace MatterHackers.MatterControl.DesignTools public bool Unlocked { get; } = true; + public IEnumerable SupportedTypes() => new Type[] { typeof(IRebuildable) }; + + private Dictionary editRows = new Dictionary(); + private static Type[] allowedTypes = { typeof(double), typeof(int), typeof(char), typeof(string), typeof(bool), @@ -65,6 +67,8 @@ namespace MatterHackers.MatterControl.DesignTools typeof(ImageObject3D) }; + private static Type IObject3DType = typeof(IObject3D); + public const BindingFlags OwnedPropertiesOnly = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; public GuiWidget Create(IObject3D item, View3DWidget view3DWidget, ThemeConfig theme) @@ -94,10 +98,6 @@ namespace MatterHackers.MatterControl.DesignTools return mainContainer; } - public IEnumerable SupportedTypes() => new Type[] { typeof(IRebuildable) }; - - Dictionary editRows = new Dictionary(); - public GuiWidget GetEditRow(string propertyName) { GuiWidget value; @@ -136,8 +136,6 @@ namespace MatterHackers.MatterControl.DesignTools return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase(); } - private static Type IObject3DType = typeof(IObject3D); - private string GetDescription(PropertyInfo prop) { var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); From 8a843faa39572225696812a9462469d0bc34b3ac Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 14:14:42 -0700 Subject: [PATCH 6/8] Revise naming to clarify behavior --- DesignTools/PublicPropertyEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs index a0c02a604..e97a9d29d 100644 --- a/DesignTools/PublicPropertyEditor.cs +++ b/DesignTools/PublicPropertyEditor.cs @@ -92,7 +92,7 @@ namespace MatterHackers.MatterControl.DesignTools if (this.item != null) { - ModifyObject(view3DWidget, mainContainer, theme); + this.CreateEditor(view3DWidget, mainContainer, theme); } return mainContainer; @@ -142,7 +142,7 @@ namespace MatterHackers.MatterControl.DesignTools return nameAttribute?.Description ?? null; } - private void ModifyObject(View3DWidget view3DWidget, FlowLayoutWidget editControlsContainer, ThemeConfig theme) + private void CreateEditor(View3DWidget view3DWidget, FlowLayoutWidget editControlsContainer, ThemeConfig theme) { var undoBuffer = view3DWidget.sceneContext.Scene.UndoBuffer; editRows.Clear(); From ece5d0fe380afe0a504bfba18c6593d8ceccf9f8 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 14:15:28 -0700 Subject: [PATCH 7/8] Wrap child PublicPropertyEditors with SectionWidget --- DesignTools/PublicPropertyEditor.cs | 2 +- PartPreviewWindow/SelectedObjectPanel.cs | 41 +++++++++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs index e97a9d29d..922bf42bd 100644 --- a/DesignTools/PublicPropertyEditor.cs +++ b/DesignTools/PublicPropertyEditor.cs @@ -130,7 +130,7 @@ namespace MatterHackers.MatterControl.DesignTools return rowContainer; } - private string GetDisplayName(PropertyInfo prop) + public static string GetDisplayName(PropertyInfo prop) { var nameAttribute = prop.GetCustomAttributes(true).OfType().FirstOrDefault(); return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase(); diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index fc971d254..3f8d88999 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -274,7 +274,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow HashSet mappedEditors = ApplicationController.Instance.GetEditorsForType(selectedItemType); - var activeEditors = new List<(IObject3DEditor, IObject3D)>(); + var activeEditors = new List<(IObject3DEditor, IObject3D, string)>(); // If item is IObject3DComponent if (componentType.IsAssignableFrom(selectedItemType)) @@ -286,7 +286,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow select new { Type = propertyType, - Value = item.GetValue(selectedItem, null) as IObject3D + Value = item.GetValue(selectedItem, null) as IObject3D, + DisplayName = PublicPropertyEditor.GetDisplayName(item) }; // Shown known editors for any matching properties @@ -294,7 +295,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) { - activeEditors.Add((editor, member.Value)); + activeEditors.Add((editor, member.Value, member.DisplayName)); } } } @@ -302,12 +303,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { // Get all public, instance properties where property type is marked with IObject3DComponentAttribute var members = from item in selectedItemType.GetProperties(PublicPropertyEditor.OwnedPropertiesOnly) - let propertyType = item.PropertyType where Attribute.IsDefined(item, componentAttribute) select new { - Type = propertyType, - Value = item.GetValue(selectedItem, null) as IObject3D + Type = item.PropertyType, + Value = item.GetValue(selectedItem, null) as IObject3D, + DisplayName = PublicPropertyEditor.GetDisplayName(item) }; // Shown known editors for any matching properties @@ -315,7 +316,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor) { - activeEditors.Add((editor, member.Value)); + activeEditors.Add((editor, member.Value, member.DisplayName)); } } } @@ -323,10 +324,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (mappedEditors?.Any() == true) { // Use first filtered or fall back to unfiltered first - activeEditors.Add((mappedEditors.First(), selectedItem)); + activeEditors.Add((mappedEditors.First(), selectedItem, null)); } - ShowObjectEditor(activeEditors); + ShowObjectEditor(activeEditors, selectedItem); } private class OperationButton :TextButton @@ -347,7 +348,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - private void ShowObjectEditor(IEnumerable<(IObject3DEditor editor, IObject3D item)> scope) + private void ShowObjectEditor(IEnumerable<(IObject3DEditor editor, IObject3D item, string displayName)> scope, IObject3D rootSelection) { editorPanel.CloseAllChildren(); @@ -364,7 +365,24 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var editorWidget = scopeItem.editor.Create(selectedItem, view3DWidget, theme); editorWidget.HAnchor = HAnchor.Stretch; editorWidget.VAnchor = VAnchor.Fit; - editorWidget.Padding = 0; + + if (scopeItem.item != rootSelection + && scopeItem.editor is PublicPropertyEditor) + { + editorWidget.Padding = new BorderDouble(10, 10, 10, 0); + + // EditOutline section + var sectionWidget = new SectionWidget( + scopeItem.displayName ?? "Unknown", + editorWidget, + theme).ApplyBoxStyle(margin: 0); + + editorWidget = sectionWidget; + } + else + { + editorWidget.Padding = 0; + } editorPanel.AddChild(editorWidget); @@ -423,7 +441,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // If the button toolbar isn't added, ensure panel has bottom margin editorWidget.Margin = editorWidget.Margin.Clone(bottom: 15); } - } } From cb3a46630fc74de3c38915a331ee76ed803c3db2 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Tue, 27 Mar 2018 14:15:38 -0700 Subject: [PATCH 8/8] Remove dead code --- PartPreviewWindow/SelectedObjectPanel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index 3f8d88999..86a0865c3 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -453,6 +453,4 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); } } - - public enum AxisAlignment { Min, Center, Max, SourceCoordinateSystem }; } \ No newline at end of file