improving image to path controls
This commit is contained in:
parent
7e22f26770
commit
69d202170c
6 changed files with 215 additions and 87 deletions
|
|
@ -33,6 +33,7 @@ using MatterHackers.Agg;
|
|||
using MatterHackers.Agg.Image;
|
||||
using MatterHackers.Agg.Image.ThresholdFunctions;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.Agg.VertexSource;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.DesignTools
|
||||
|
|
@ -42,9 +43,9 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
private ImageBuffer _histogramRawCache = new ImageBuffer(256, 100);
|
||||
private ThemeConfig theme;
|
||||
|
||||
public double RangeStart { get; set; } = .1;
|
||||
public double RangeStart { get; set; } = 0;
|
||||
|
||||
public double RangeEnd { get; set; } = 1;
|
||||
public double RangeEnd { get; set; } = .9;
|
||||
|
||||
private Color GetRGBA(byte[] buffer, int offset)
|
||||
{
|
||||
|
|
@ -52,6 +53,8 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
|
||||
public event EventHandler RangeChanged;
|
||||
|
||||
public event EventHandler EditComplete;
|
||||
|
||||
public void RebuildAlphaImage(ImageBuffer sourceImage, ImageBuffer alphaImage)
|
||||
{
|
||||
|
|
@ -97,7 +100,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
byte[] sourceBuffer = sourceImage.GetBuffer();
|
||||
byte[] destBuffer = alphaImage.GetBuffer();
|
||||
for (int y = 0; y < sourceImage.Height; y++)
|
||||
Parallel.For(0, sourceImage.Height, (y) =>
|
||||
{
|
||||
int imageOffset = sourceImage.GetBufferOffsetY(y);
|
||||
|
||||
|
|
@ -112,7 +115,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
destBuffer[imageBufferOffsetWithX + 2] = b;
|
||||
destBuffer[imageBufferOffsetWithX + 3] = GetAlphaFromIntensity(r, g, b);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
alphaImage.MarkImageChanged();
|
||||
}
|
||||
|
|
@ -122,7 +125,8 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
// build the histogram cache
|
||||
_histogramRawCache = new ImageBuffer(256, 100);
|
||||
var counts = new int[_histogramRawCache.Width];
|
||||
var function = new MapOnMaxIntensity(RangeStart, RangeEnd);
|
||||
IThresholdFunction function = new MapOnMaxIntensity(RangeStart, RangeEnd);
|
||||
function = new HueThresholdFunction(RangeStart, RangeEnd);
|
||||
|
||||
byte[] buffer = image.GetBuffer();
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
|
|
@ -141,7 +145,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
.OrderByDescending(vi => vi.value)
|
||||
.First().value;
|
||||
var graphics2D2 = _histogramRawCache.NewGraphics2D();
|
||||
graphics2D2.Clear(Color.White);
|
||||
graphics2D2.Clear(ApplicationController.Instance.Theme.SlightShade);
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
graphics2D2.Line(i, 0, i, Easing.Exponential.Out(counts[i] / max) * _histogramRawCache.Height, Color.Black);
|
||||
|
|
@ -156,7 +160,6 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
HAnchor = HAnchor.Stretch,
|
||||
Height = 60 * GuiWidget.DeviceScale,
|
||||
Margin = 5,
|
||||
BackgroundColor = theme.SlightShade
|
||||
};
|
||||
|
||||
var handleWidth = 10 * GuiWidget.DeviceScale;
|
||||
|
|
@ -167,15 +170,39 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
Margin = new BorderDouble(handleWidth, 0)
|
||||
};
|
||||
|
||||
histogramBackground.AfterDraw += HistogramBackground_AfterDraw;
|
||||
histogramBackground.AfterDraw += (s, e) =>
|
||||
{
|
||||
var rangeStart = RangeStart;
|
||||
var rangeEnd = RangeEnd;
|
||||
var graphics2D = e.Graphics2D;
|
||||
graphics2D.Render(_histogramRawCache, 0, 0);
|
||||
var background = _histogramRawCache;
|
||||
graphics2D.FillRectangle(rangeStart * background.Width, 0, rangeEnd * background.Width, background.Height, theme.PrimaryAccentColor.WithAlpha(60));
|
||||
};
|
||||
|
||||
histogramWidget.AddChild(histogramBackground);
|
||||
|
||||
void RenderHandle(Graphics2D g, double s, double e)
|
||||
{
|
||||
var w = g.Width;
|
||||
var h = g.Height;
|
||||
g.Line(w * e, 0, w * e, h, theme.TextColor);
|
||||
var leftEdge = new VertexStorage();
|
||||
leftEdge.MoveTo(w * e, h * .80);
|
||||
leftEdge.curve3(w * e, h * .70, w * .5, h * .70);
|
||||
leftEdge.curve3(w * s, h * .60);
|
||||
leftEdge.LineTo(w * s, h * .40);
|
||||
leftEdge.curve3(w * s, h * .30, w * .5, h * .30);
|
||||
leftEdge.curve3(w * e, h * .20);
|
||||
g.Render(new FlattenCurves(leftEdge), theme.TextColor);
|
||||
g.Line(w * .35, h * .6, w * .35, h * .4, theme.BackgroundColor);
|
||||
g.Line(w * .65, h * .6, w * .65, h * .4, theme.BackgroundColor);
|
||||
}
|
||||
|
||||
var leftHandle = new ImageWidget((int)(handleWidth), (int)histogramWidget.Height);
|
||||
leftHandle.Position = new Vector2(RangeStart * _histogramRawCache.Width, 0);
|
||||
var image = leftHandle.Image;
|
||||
var leftGraphics = image.NewGraphics2D();
|
||||
leftGraphics.Line(image.Width, 0, image.Width, image.Height, theme.TextColor);
|
||||
leftGraphics.FillRectangle(0, image.Height / 4, image.Width, image.Height / 4 * 3, theme.TextColor);
|
||||
RenderHandle(image.NewGraphics2D(), 0, 1);
|
||||
histogramWidget.AddChild(leftHandle);
|
||||
|
||||
bool leftDown = false;
|
||||
|
|
@ -193,23 +220,29 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
if (leftDown)
|
||||
{
|
||||
var offset = e.Position.X - leftX;
|
||||
RangeStart += offset / _histogramRawCache.Width;
|
||||
RangeStart = Math.Max(0, Math.Min(RangeStart, RangeEnd));
|
||||
leftHandle.Position = new Vector2(RangeStart * _histogramRawCache.Width, 0);
|
||||
RangeChanged?.Invoke(this, null);
|
||||
var newStart = RangeStart + offset / _histogramRawCache.Width;
|
||||
newStart = agg_basics.Clamp(newStart, 0, RangeEnd);
|
||||
if (RangeStart != newStart)
|
||||
{
|
||||
RangeStart = newStart;
|
||||
leftHandle.Position = new Vector2(RangeStart * _histogramRawCache.Width, 0);
|
||||
RangeChanged?.Invoke(this, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
leftHandle.MouseUp += (s, e) =>
|
||||
{
|
||||
leftDown = false;
|
||||
if (leftDown)
|
||||
{
|
||||
leftDown = false;
|
||||
EditComplete?.Invoke(this, null);
|
||||
}
|
||||
};
|
||||
|
||||
var rightHandle = new ImageWidget((int)(handleWidth), (int)histogramWidget.Height);
|
||||
rightHandle.Position = new Vector2(RangeEnd * _histogramRawCache.Width + handleWidth, 0);
|
||||
image = rightHandle.Image;
|
||||
var rightGraphics = image.NewGraphics2D();
|
||||
rightGraphics.Line(0, 0, 0, image.Height, theme.TextColor);
|
||||
rightGraphics.FillRectangle(0, image.Height / 4, image.Width, image.Height / 4 * 3, theme.TextColor);
|
||||
RenderHandle(image.NewGraphics2D(), 1, 0);
|
||||
histogramWidget.AddChild(rightHandle);
|
||||
|
||||
bool rightDown = false;
|
||||
|
|
@ -227,28 +260,26 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
if (rightDown)
|
||||
{
|
||||
var offset = e.Position.X - rightX;
|
||||
RangeEnd += offset / _histogramRawCache.Width;
|
||||
RangeEnd = Math.Min(1, Math.Max(RangeStart, RangeEnd));
|
||||
rightHandle.Position = new Vector2(RangeEnd * _histogramRawCache.Width + handleWidth, 0);
|
||||
RangeChanged?.Invoke(this, null);
|
||||
var newEnd = RangeEnd + offset / _histogramRawCache.Width;
|
||||
newEnd = agg_basics.Clamp(newEnd, RangeStart, 1);
|
||||
if (RangeEnd != newEnd)
|
||||
{
|
||||
RangeEnd = newEnd;
|
||||
rightHandle.Position = new Vector2(RangeEnd * _histogramRawCache.Width + handleWidth, 0);
|
||||
RangeChanged?.Invoke(this, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
rightHandle.MouseUp += (s, e) =>
|
||||
{
|
||||
rightDown = false;
|
||||
if (rightDown)
|
||||
{
|
||||
rightDown = false;
|
||||
EditComplete?.Invoke(this, null);
|
||||
}
|
||||
};
|
||||
|
||||
return histogramWidget;
|
||||
}
|
||||
|
||||
private void HistogramBackground_AfterDraw(object sender, DrawEventArgs e)
|
||||
{
|
||||
var rangeStart = RangeStart;
|
||||
var rangeEnd = RangeEnd;
|
||||
var graphics2D = e.Graphics2D;
|
||||
graphics2D.Render(_histogramRawCache, 0, 0);
|
||||
var background = _histogramRawCache;
|
||||
graphics2D.FillRectangle(rangeStart * background.Width, 0, rangeEnd * background.Width, background.Height, theme.PrimaryAccentColor.WithAlpha(60));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project.
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -51,14 +52,14 @@ using Polygons = System.Collections.Generic.List<System.Collections.Generic.List
|
|||
namespace MatterHackers.MatterControl.DesignTools
|
||||
{
|
||||
[HideMeterialAndColor]
|
||||
public class ImageToPathObject3D_2 : Object3D, IImageProvider, IPathObject, ISelectedEditorDraw, IObject3DControlsProvider
|
||||
public class ImageToPathObject3D_2 : Object3D, IImageProvider, IPathObject, ISelectedEditorDraw, IObject3DControlsProvider, IPropertyGridModifier
|
||||
{
|
||||
public ImageToPathObject3D_2()
|
||||
{
|
||||
Name = "Image to Path".Localize();
|
||||
}
|
||||
|
||||
public enum ThresholdFunctions
|
||||
public enum FeatureDetectors
|
||||
{
|
||||
Transparency,
|
||||
Colors,
|
||||
|
|
@ -75,14 +76,31 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_image == null)
|
||||
if (_image == null
|
||||
&& SourceImage != null)
|
||||
{
|
||||
_image = new ImageBuffer(SourceImage);
|
||||
IntensityHistogram.RebuildAlphaImage(SourceImage, _image);
|
||||
IntensityHistogram.BuildHistogramFromImage(SourceImage);
|
||||
IntensityHistogram.RangeChanged += (s, e) =>
|
||||
{
|
||||
IntensityHistogram.RebuildAlphaImage(SourceImage, _image);
|
||||
};
|
||||
|
||||
IntensityHistogram.EditComplete += (s, e) =>
|
||||
{
|
||||
this.Invalidate(InvalidateType.Properties);
|
||||
};
|
||||
|
||||
switch (FeatureDetector)
|
||||
{
|
||||
case FeatureDetectors.Intensity:
|
||||
IntensityHistogram.RebuildAlphaImage(SourceImage, _image);
|
||||
break;
|
||||
|
||||
case FeatureDetectors.Transparency:
|
||||
_image.CopyFrom(SourceImage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _image;
|
||||
|
|
@ -94,8 +112,38 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
|
||||
|
||||
private FeatureDetectors _featureDetector = FeatureDetectors.Intensity;
|
||||
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Tabs)]
|
||||
public ThresholdFunctions FeatureDetector { get; set; } = ThresholdFunctions.Intensity;
|
||||
public FeatureDetectors FeatureDetector
|
||||
{
|
||||
get
|
||||
{
|
||||
return _featureDetector;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_featureDetector != value)
|
||||
{
|
||||
_featureDetector = value;
|
||||
switch (FeatureDetector)
|
||||
{
|
||||
case FeatureDetectors.Intensity:
|
||||
IntensityHistogram.RebuildAlphaImage(SourceImage, Image);
|
||||
break;
|
||||
|
||||
case FeatureDetectors.Transparency:
|
||||
Image?.CopyFrom(SourceImage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DisplayName("")]
|
||||
[ReadOnly(true)]
|
||||
public string TransparencyMessage { get; set; } = "Your image is processed as is with no modifications. Transparent pixels are ignored, only opaque pixels are considered in feature detection.";
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
private ImageBuffer SourceImage => ((IImageProvider)this.Descendants().Where(i => i is IImageProvider).FirstOrDefault())?.Image;
|
||||
|
|
@ -189,6 +237,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
&& !RebuildLocked)
|
||||
{
|
||||
IntensityHistogram.BuildHistogramFromImage(SourceImage);
|
||||
IntensityHistogram.RebuildAlphaImage(SourceImage, _image);
|
||||
await Rebuild();
|
||||
}
|
||||
else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this))
|
||||
|
|
@ -217,7 +266,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
var progressStatus = new ProgressStatus();
|
||||
switch (FeatureDetector)
|
||||
{
|
||||
case ThresholdFunctions.Transparency:
|
||||
case FeatureDetectors.Transparency:
|
||||
this.GenerateMarchingSquaresAndLines(
|
||||
(progress0to1, status) =>
|
||||
{
|
||||
|
|
@ -229,7 +278,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
new AlphaFunction());
|
||||
break;
|
||||
|
||||
case ThresholdFunctions.Intensity:
|
||||
case FeatureDetectors.Intensity:
|
||||
this.GenerateMarchingSquaresAndLines(
|
||||
(progress0to1, status) =>
|
||||
{
|
||||
|
|
@ -251,5 +300,11 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateControls(PublicPropertyChange change)
|
||||
{
|
||||
change.SetRowVisible(nameof(IntensityHistogram), () => FeatureDetector == FeatureDetectors.Intensity);
|
||||
change.SetRowVisible(nameof(TransparencyMessage), () => FeatureDetector == FeatureDetectors.Transparency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,8 +36,10 @@ using MatterHackers.Agg;
|
|||
using MatterHackers.Agg.Image;
|
||||
using MatterHackers.Agg.ImageProcessing;
|
||||
using MatterHackers.Agg.Platform;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DataStorage;
|
||||
using MatterHackers.MatterControl.PartPreviewWindow;
|
||||
using MatterHackers.PolygonMesh;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -240,5 +242,45 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void AddEditorExtra(GuiWidget imageWidget, ThemeConfig theme, Action updateEditorImage)
|
||||
{
|
||||
imageWidget.Click += (s, e) =>
|
||||
{
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
var popupMenu = new PopupMenu(theme);
|
||||
|
||||
var pasteMenu = popupMenu.CreateMenuItem("Paste".Localize());
|
||||
pasteMenu.Click += (s2, e2) =>
|
||||
{
|
||||
var activeImage = Clipboard.Instance.GetImage();
|
||||
|
||||
// Persist
|
||||
string filePath = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".png");
|
||||
ImageIO.SaveImageData(
|
||||
filePath,
|
||||
activeImage);
|
||||
|
||||
this.AssetPath = filePath;
|
||||
this.Mesh = null;
|
||||
|
||||
updateEditorImage();
|
||||
|
||||
this.Invalidate(InvalidateType.Image);
|
||||
};
|
||||
|
||||
pasteMenu.Enabled = Clipboard.Instance.ContainsImage;
|
||||
|
||||
var copyMenu = popupMenu.CreateMenuItem("Copy".Localize());
|
||||
copyMenu.Click += (s2, e2) =>
|
||||
{
|
||||
Clipboard.Instance.SetImage(this.Image);
|
||||
};
|
||||
|
||||
popupMenu.ShowMenu(imageWidget, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -618,6 +618,24 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
imageWidget.Margin = new BorderDouble(0, 3);
|
||||
}
|
||||
|
||||
imageWidget.BeforeDraw += (s, e) =>
|
||||
{
|
||||
// render a checkerboard that can show through the alpha mask
|
||||
var g = e.Graphics2D;
|
||||
var w = (int)(10 * GuiWidget.DeviceScale);
|
||||
for (int x = 0; x < g.Width / w; x ++)
|
||||
{
|
||||
for (int y = 0; y < g.Height / w; y ++)
|
||||
{
|
||||
if (y % 2 == 0 && x % 2 == 1
|
||||
|| y % 2 == 1 && x % 2 == 0)
|
||||
{
|
||||
g.FillRectangle(x * w, y * w, x * w + w, y * w + w, Color.LightGray);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ImageBuffer GetImageCheckingForErrors()
|
||||
{
|
||||
var image = imageBuffer;
|
||||
|
|
@ -665,42 +683,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
if (object3D is ImageObject3D imageObject)
|
||||
{
|
||||
imageWidget.Click += (s, e) =>
|
||||
{
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
var popupMenu = new PopupMenu(theme);
|
||||
|
||||
var pasteMenu = popupMenu.CreateMenuItem("Paste".Localize());
|
||||
pasteMenu.Click += (s2, e2) =>
|
||||
{
|
||||
var activeImage = Clipboard.Instance.GetImage();
|
||||
|
||||
// Persist
|
||||
string filePath = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".png");
|
||||
ImageIO.SaveImageData(
|
||||
filePath,
|
||||
activeImage);
|
||||
|
||||
imageObject.AssetPath = filePath;
|
||||
imageObject.Mesh = null;
|
||||
|
||||
UpdateEditorImage();
|
||||
|
||||
imageObject.Invalidate(InvalidateType.Image);
|
||||
};
|
||||
|
||||
pasteMenu.Enabled = Clipboard.Instance.ContainsImage;
|
||||
|
||||
var copyMenu = popupMenu.CreateMenuItem("Copy".Localize());
|
||||
copyMenu.Click += (s2, e2) =>
|
||||
{
|
||||
Clipboard.Instance.SetImage(imageObject.Image);
|
||||
};
|
||||
|
||||
popupMenu.ShowMenu(imageWidget, e);
|
||||
}
|
||||
};
|
||||
imageObject.AddEditorExtra(imageWidget, theme, UpdateEditorImage);
|
||||
}
|
||||
|
||||
rowContainer.AddChild(imageWidget);
|
||||
|
|
|
|||
|
|
@ -249,29 +249,46 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
["index2"] = (owner) => RetrieveArrayIndex(owner, 2),
|
||||
};
|
||||
|
||||
private static ArrayObject3D FindParentArray(IObject3D item, int level)
|
||||
private static ArrayObject3D FindParentArray(IObject3D item, int wantLevel)
|
||||
{
|
||||
int foundLevel = 0;
|
||||
// look through all the parents
|
||||
foreach (var parent in item.Parents())
|
||||
{
|
||||
// then each child of any give parent
|
||||
foreach (var sibling in parent.Children)
|
||||
// if it is a sheet
|
||||
if (parent is ArrayObject3D arrayObject)
|
||||
{
|
||||
// if it is a sheet
|
||||
if (sibling != item
|
||||
&& sibling is SheetObject3D sheet)
|
||||
if (foundLevel == wantLevel)
|
||||
{
|
||||
return sheet;
|
||||
return arrayObject;
|
||||
}
|
||||
|
||||
foundLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double RetrieveArrayIndex(IObject3D owner, int level)
|
||||
private static int RetrieveArrayIndex(IObject3D owner, int level)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var arrayObject = FindParentArray(owner, level);
|
||||
|
||||
if (arrayObject != null)
|
||||
{
|
||||
int index = 0;
|
||||
foreach(var child in arrayObject.Children)
|
||||
{
|
||||
if (child == owner)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static string ReplaceConstantsWithValues(IObject3D owner, string stringWithConstants)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 3355908f5e59e8f0afa64478111071d656ab5d47
|
||||
Subproject commit 2ec468c986a2e2a4b65fc092e120760d3f0e1a2d
|
||||
Loading…
Add table
Add a link
Reference in a new issue