Merge pull request #3118 from jlewin/design_tools
Use existing editor system
This commit is contained in:
commit
f81b15bb32
7 changed files with 388 additions and 89 deletions
|
|
@ -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
194
DesignTools/Lithophane.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
91
DesignTools/LithophaneObject3D.cs
Normal file
91
DesignTools/LithophaneObject3D.cs
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue