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/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/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs
index 43c06e7a9..922bf42bd 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)
@@ -88,16 +92,12 @@ namespace MatterHackers.MatterControl.DesignTools
if (this.item != null)
{
- ModifyObject(view3DWidget, mainContainer, theme);
+ this.CreateEditor(view3DWidget, mainContainer, theme);
}
return mainContainer;
}
- public IEnumerable SupportedTypes() => new Type[] { typeof(IRebuildable) };
-
- Dictionary editRows = new Dictionary();
-
public GuiWidget GetEditRow(string propertyName)
{
GuiWidget value;
@@ -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();
@@ -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();
@@ -457,11 +457,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);
}
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/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs
index 086dc92ee..86a0865c3 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,9 +272,9 @@ 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)>();
+ var activeEditors = new List<(IObject3DEditor, IObject3D, string)>();
// If item is IObject3DComponent
if (componentType.IsAssignableFrom(selectedItemType))
@@ -307,15 +286,16 @@ 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
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));
+ activeEditors.Add((editor, member.Value, member.DisplayName));
}
}
}
@@ -323,20 +303,20 @@ 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
- foreach (var member in members)
+ foreach (var member in members.Where(m => m.Value != null))
{
- if (this.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor)
+ if (ApplicationController.Instance.GetEditorsForType(member.Type)?.FirstOrDefault() is IObject3DEditor editor)
{
- activeEditors.Add((editor, member.Value));
+ activeEditors.Add((editor, member.Value, member.DisplayName));
}
}
}
@@ -344,32 +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);
- }
-
- 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;
+ ShowObjectEditor(activeEditors, selectedItem);
}
private class OperationButton :TextButton
@@ -390,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();
@@ -407,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);
@@ -466,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);
}
-
}
}
@@ -479,6 +453,4 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
});
}
}
-
- public enum AxisAlignment { Min, Center, Max, SourceCoordinateSystem };
}
\ No newline at end of file
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)