Merge pull request #3118 from jlewin/design_tools

Use existing editor system
This commit is contained in:
johnlewin 2018-03-27 15:09:44 -07:00 committed by GitHub
commit f81b15bb32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 388 additions and 89 deletions

View file

@ -73,7 +73,7 @@ namespace MatterHackers.MatterControl
public class AppContext
{
/// <summary>
/// Native platform features
/// Native platform features
/// </summary>
public static INativePlatformFeatures Platform { get; set; }
@ -87,6 +87,8 @@ namespace MatterHackers.MatterControl
public class ApplicationController
{
private Dictionary<Type, HashSet<IObject3DEditor>> 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<IObject3D> { selectedItem }, new List<IObject3D> { 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<IObject3DEditor> mappedEditors;
objectEditorsByType = new Dictionary<Type, HashSet<IObject3DEditor>>();
foreach (IObject3DEditor editor in PluginFinder.CreateInstancesOf<IObject3DEditor>())
{
foreach (Type type in editor.SupportedTypes())
{
if (!objectEditorsByType.TryGetValue(type, out mappedEditors))
{
mappedEditors = new HashSet<IObject3DEditor>();
objectEditorsByType.Add(type, mappedEditors);
}
mappedEditors.Add(editor);
}
}
}
public HashSet<IObject3DEditor> GetEditorsForType(Type selectedItemType)
{
HashSet<IObject3DEditor> 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);

194
DesignTools/Lithophane.cs Normal file
View file

@ -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<ProgressStatus> 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();
}
}
}

View file

@ -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<ImageObject3D>().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;
});
}
}
}

View file

@ -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<Type> SupportedTypes() => new Type[] { typeof(IRebuildable) };
private Dictionary<string, GuiWidget> editRows = new Dictionary<string, GuiWidget>();
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<Type> SupportedTypes() => new Type[] { typeof(IRebuildable) };
Dictionary<string, GuiWidget> editRows = new Dictionary<string, GuiWidget>();
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<DisplayNameAttribute>().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);
}

View file

@ -94,6 +94,7 @@
<Compile Include="DesignTools\Interfaces\IEditorDraw.cs" />
<Compile Include="DesignTools\Interfaces\IPropertyGridModifier.cs" />
<Compile Include="DesignTools\Interfaces\IRebuildable.cs" />
<Compile Include="DesignTools\Lithophane.cs" />
<Compile Include="DesignTools\Operations\Align3D.cs" />
<Compile Include="DesignTools\Operations\ArrayAdvanced3D.cs" />
<Compile Include="DesignTools\Operations\ArrayLinear3D.cs" />
@ -131,6 +132,7 @@
<Compile Include="CustomWidgets\InlineTitleEdit.cs" />
<Compile Include="Library\DynamicContentStore.cs" />
<Compile Include="Library\InMemoryLibraryItem.cs" />
<Compile Include="DesignTools\LithophaneObject3D.cs" />
<Compile Include="PartPreviewWindow\LibraryBrowserPage.cs" />
<Compile Include="PartPreviewWindow\MaterialControls.cs" />
<Compile Include="PartPreviewWindow\MoveItemPage.cs" />

View file

@ -61,7 +61,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private View3DWidget view3DWidget;
private InteractiveScene scene;
private PrinterConfig printer;
private Dictionary<Type, HashSet<IObject3DEditor>> 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<IObject3DEditor> mappedEditors;
objectEditorsByType = new Dictionary<Type, HashSet<IObject3DEditor>>();
// TODO: Consider only loading once into a static
var objectEditors = PluginFinder.CreateInstancesOf<IObject3DEditor>();
foreach (IObject3DEditor editor in objectEditors)
{
foreach (Type type in editor.SupportedTypes())
{
if (!objectEditorsByType.TryGetValue(type, out mappedEditors))
{
mappedEditors = new HashSet<IObject3DEditor>();
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<IObject3DEditor> mappedEditors = GetEditorsForType(selectedItemType);
HashSet<IObject3DEditor> 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<IObject3DEditor> GetEditorsForType(Type selectedItemType)
{
HashSet<IObject3DEditor> 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 };
}

View file

@ -49,6 +49,8 @@ namespace MatterHackers.MatterControl.DesignTools
string IObject3DEditor.Name => "Image Editor";
IEnumerable<Type> 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<Type> IObject3DEditor.SupportedTypes() => new[] { typeof(ImageObject3D) };
private ImageBuffer ScaleThumbnailImage(int height, ImageBuffer imageBuffer)
{
if (imageBuffer.Height != height)