Improving path editor

This commit is contained in:
MatterHackers 2023-11-05 17:26:43 -08:00
parent dfb5700bf8
commit bb8d8b3859
6 changed files with 268 additions and 75 deletions

View file

@ -75,16 +75,11 @@ namespace MatterHackers.MatterControl.DesignTools
private void VertexBufferChanged() private void VertexBufferChanged()
{ {
object3D.Invalidate(InvalidateType.Path); object3D.Invalidate(new InvalidateArgs(null, InvalidateType.Path));
} }
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class TopAndBottomMoveXOnlyAttribute : Attribute public class ShowAxisAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class XMustBeGreaterThan0Attribute : Attribute
{ {
} }
} }

View file

@ -34,15 +34,23 @@ using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource; using MatterHackers.Agg.VertexSource;
using MatterHackers.ImageProcessing; using MatterHackers.ImageProcessing;
using MatterHackers.Localizations; using MatterHackers.Localizations;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath; using MatterHackers.VectorMath;
using System; using System;
using static MatterHackers.MatterControl.PartPreviewWindow.GCode2DWidget;
namespace MatterHackers.MatterControl.DesignTools namespace MatterHackers.MatterControl.DesignTools
{ {
public class PathEditorWidget : GuiWidget public class PathEditorWidget : GuiWidget
{ {
public enum ETransformState
{
Edit,
Move,
Scale
};
private Vector2 lastMousePosition = new Vector2(0, 0); private Vector2 lastMousePosition = new Vector2(0, 0);
private ETransformState mouseDownTransformOverride;
private Vector2 mouseDownPosition = new Vector2(0, 0); private Vector2 mouseDownPosition = new Vector2(0, 0);
private Action<Vector2, double> scaleChanged; private Action<Vector2, double> scaleChanged;
private ThemeConfig theme; private ThemeConfig theme;
@ -56,7 +64,7 @@ namespace MatterHackers.MatterControl.DesignTools
UndoBuffer undoBuffer, UndoBuffer undoBuffer,
ThemeConfig theme, ThemeConfig theme,
Action vertexChanged, Action vertexChanged,
Vector2 unscaledRenderOffset = default(Vector2), Vector2 unscaledRenderOffset = default,
double layerScale = 1, double layerScale = 1,
Action<Vector2, double> scaleChanged = null) Action<Vector2, double> scaleChanged = null)
{ {
@ -85,13 +93,76 @@ namespace MatterHackers.MatterControl.DesignTools
var toolBar = new FlowLayoutWidget() var toolBar = new FlowLayoutWidget()
{ {
HAnchor = HAnchor.Stretch, HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Bottom Margin = 5,
BackgroundColor= theme.TextColor.WithAlpha(20),
}; };
toolBar.VAnchor |= VAnchor.Bottom;
this.AddChild(toolBar); this.AddChild(toolBar);
var menuTheme = ApplicationController.Instance.MenuTheme; AddHomeButton(theme, toolBar);
var homeButton = new ThemedTextIconButton("Home".Localize(), StaticData.Instance.LoadIcon("fa-home_16.png", 16, 16).GrayToColor(menuTheme.TextColor), theme)
toolBar.AddChild(new HorizontalSpacer());
AddPositionControls(theme, toolBar);
}
public static readonly int VectorXYEditWidth = (int)(60 * GuiWidget.DeviceScale + .5);
private void AddPositionControls(ThemeConfig theme, FlowLayoutWidget toolBar)
{
var tabIndex = 0;
var xEditWidget = new ThemedNumberEdit(0, theme, singleCharLabel: 'X', allowNegatives: true, allowDecimals: true, pixelWidth: VectorXYEditWidth, tabIndex: tabIndex)
{
TabIndex = tabIndex++,
SelectAllOnFocus = true,
Margin = theme.ButtonSpacing,
VAnchor = VAnchor.Center,
};
xEditWidget.ActuallNumberEdit.EditComplete += (sender, e) =>
{
if (controlPointBeingDragged > -1)
{
var vertexData = vertexStorage[controlPointBeingDragged];
if (vertexData.Hint == CommandHint.C4Point)
{
// the prev point
if (controlPointBeingDragged > 0)
{
controlPointBeingDragged--;
vertexData = new VertexData(vertexData.Command, new Vector2(vertexData.Position.X, xEditWidget.ActuallNumberEdit.Value), vertexData.Hint);
controlPointBeingDragged++;
}
}
else
{
}
}
};
xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
toolBar.AddChild(xEditWidget);
var yEditWidget = new ThemedNumberEdit(0, theme, 'Y', allowNegatives: true, allowDecimals: true, pixelWidth: VectorXYEditWidth, tabIndex: tabIndex)
{
TabIndex = tabIndex++,
SelectAllOnFocus = true,
VAnchor = VAnchor.Center,
Margin = theme.ButtonSpacing,
};
yEditWidget.ActuallNumberEdit.EditComplete += (sender, e) =>
{
};
yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
toolBar.AddChild(yEditWidget);
}
private void AddHomeButton(ThemeConfig theme, FlowLayoutWidget toolBar)
{
var homeButton = new ThemedIconButton(StaticData.Instance.LoadIcon("fa-home_16.png", 16, 16).GrayToColor(theme.TextColor), theme)
{ {
BackgroundColor = theme.SlightShade, BackgroundColor = theme.SlightShade,
HoverColor = theme.SlightShade.WithAlpha(75), HoverColor = theme.SlightShade.WithAlpha(75),
@ -102,10 +173,7 @@ namespace MatterHackers.MatterControl.DesignTools
homeButton.Click += (s, e) => homeButton.Click += (s, e) =>
{ {
UiThread.RunOnIdle(() => CenterPartInView();
{
ApplicationController.LaunchBrowser("https://www.matterhackers.com/store/c/3d-printer-filament");
});
}; };
} }
@ -115,6 +183,23 @@ namespace MatterHackers.MatterControl.DesignTools
private Affine ScalingTransform => Affine.NewScaling(layerScale, layerScale); private Affine ScalingTransform => Affine.NewScaling(layerScale, layerScale);
private Affine TotalTransform => Affine.NewTranslation(unscaledRenderOffset) * ScalingTransform * Affine.NewTranslation(Width / 2, Height / 2); private Affine TotalTransform => Affine.NewTranslation(unscaledRenderOffset) * ScalingTransform * Affine.NewTranslation(Width / 2, Height / 2);
private int controlPointBeingDragged = -1;
private int controlPointBeingHovered = -1;
public void CenterPartInView()
{
if (vertexStorage != null)
{
var partBounds = vertexStorage.GetBounds();
var weightedCenter = partBounds.Center;
unscaledRenderOffset = -weightedCenter;
layerScale = Math.Min((Height - 30) / partBounds.Height, (Width - 30) / partBounds.Width);
Invalidate();
}
}
public override void OnDraw(Graphics2D graphics2D) public override void OnDraw(Graphics2D graphics2D)
{ {
new VertexSourceApplyTransform(vertexStorage, TotalTransform).RenderCurve(graphics2D, theme.TextColor, 2, true, theme.PrimaryAccentColor.Blend(theme.TextColor, .5), theme.PrimaryAccentColor); new VertexSourceApplyTransform(vertexStorage, TotalTransform).RenderCurve(graphics2D, theme.TextColor, 2, true, theme.PrimaryAccentColor.Blend(theme.TextColor, .5), theme.PrimaryAccentColor);
@ -127,59 +212,170 @@ namespace MatterHackers.MatterControl.DesignTools
base.OnMouseDown(mouseEvent); base.OnMouseDown(mouseEvent);
if (MouseCaptured) if (MouseCaptured)
{ {
controlPointBeingDragged = -1;
mouseDownPosition.X = mouseEvent.X; mouseDownPosition.X = mouseEvent.X;
mouseDownPosition.Y = mouseEvent.Y; mouseDownPosition.Y = mouseEvent.Y;
lastMousePosition = mouseDownPosition; lastMousePosition = mouseDownPosition;
mouseDownTransformOverride = TransformState;
// check if not left button
switch (mouseEvent.Button)
{
case MouseButtons.Left:
if (Keyboard.IsKeyDown(Keys.ControlKey))
{
if (Keyboard.IsKeyDown(Keys.Alt))
{
mouseDownTransformOverride = ETransformState.Scale;
}
else
{
mouseDownTransformOverride = ETransformState.Move;
}
}
else
{
// we are in edit mode, check if we are over any control points
controlPointBeingDragged = GetControlPointIndex(mouseEvent.Position);
}
break;
case MouseButtons.Middle:
mouseDownTransformOverride = ETransformState.Move;
break;
case MouseButtons.Right:
mouseDownTransformOverride = ETransformState.Scale;
break;
}
} }
} }
private int GetControlPointIndex(Vector2 mousePosition)
{
double hitThreshold = 10; // Threshold for considering a hit, in screen pixels
for (int i = 0; i < vertexStorage.Count; i++)
{
Vector2 controlPoint = vertexStorage[i].Position + unscaledRenderOffset;
ScalingTransform.transform(ref controlPoint);
// we center on the scren so we have to add that in after scaling
controlPoint += new Vector2(Width / 2, Height / 2);
if ((controlPoint - mousePosition).Length <= hitThreshold) // Check if the mouse position is within the threshold
{
return i; // Control point index
}
}
return -1; // No control point found at this position
}
public override void OnMouseMove(MouseEventArgs mouseEvent) public override void OnMouseMove(MouseEventArgs mouseEvent)
{ {
base.OnMouseMove(mouseEvent); base.OnMouseMove(mouseEvent);
var mousePos = new Vector2(mouseEvent.X, mouseEvent.Y);
if (MouseCaptured) if (MouseCaptured)
{ {
var mouseDelta = mousePos - lastMousePosition; DoTranslateAndZoom(mouseEvent);
switch (TransformState)
if (controlPointBeingDragged > -1)
{ {
case ETransformState.Scale: // we are dragging a control point
double zoomDelta = 1; var mouseDelta = mouseEvent.Position - lastMousePosition;
if (mouseDelta.Y < 0) if (mouseDelta.LengthSquared > 0)
{ {
zoomDelta = 1 - (-1 * mouseDelta.Y / 100);
}
else if (mouseDelta.Y > 0)
{
zoomDelta = 1 + (1 * mouseDelta.Y / 100);
}
var mousePreScale = mouseDownPosition;
TotalTransform.inverse_transform(ref mousePreScale);
layerScale *= zoomDelta;
var mousePostScale = mouseDownPosition;
TotalTransform.inverse_transform(ref mousePostScale);
unscaledRenderOffset += (mousePostScale - mousePreScale);
scaleChanged?.Invoke(unscaledRenderOffset, layerScale);
break;
case ETransformState.Move:
default: // also treat everything else like a move
ScalingTransform.inverse_transform(ref mouseDelta); ScalingTransform.inverse_transform(ref mouseDelta);
OffsetSelectedPoint(mouseDelta);
unscaledRenderOffset += mouseDelta; vertexChanged?.Invoke();
scaleChanged?.Invoke(unscaledRenderOffset, layerScale); }
break;
} }
}
Invalidate(); else
{
// highlight any contorl points we are over
} }
lastMousePosition = mousePos; lastMousePosition = mouseEvent.Position;
}
private void OffsetSelectedPoint(Vector2 delta)
{
if (controlPointBeingDragged < 0
|| controlPointBeingDragged >= vertexStorage.Count)
{
return;
}
var vertexData = vertexStorage[controlPointBeingDragged];
if (vertexData.Hint == CommandHint.C4Point)
{
for (int i = -1; i < 2; i++)
{
var pointIndex = controlPointBeingDragged + i;
// the prev point
if (pointIndex > 0
&& pointIndex < vertexStorage.Count)
{
var vertexData2 = vertexStorage[pointIndex];
vertexStorage[pointIndex] = new VertexData(vertexData2.Command, vertexData2.Position + delta, vertexData2.Hint);
}
}
}
else
{
// only drag the point
vertexStorage[controlPointBeingDragged] = new VertexData(vertexData.Command, vertexData.Position + delta, vertexData.Hint);
}
}
private void DoTranslateAndZoom(MouseEventArgs mouseEvent)
{
var mousePos = new Vector2(mouseEvent.X, mouseEvent.Y);
var mouseDelta = mousePos - lastMousePosition;
switch (mouseDownTransformOverride)
{
case ETransformState.Scale:
double zoomDelta = 1;
if (mouseDelta.Y < 0)
{
zoomDelta = 1 - (-1 * mouseDelta.Y / 100);
}
else if (mouseDelta.Y > 0)
{
zoomDelta = 1 + (1 * mouseDelta.Y / 100);
}
var mousePreScale = mouseDownPosition;
TotalTransform.inverse_transform(ref mousePreScale);
layerScale *= zoomDelta;
var mousePostScale = mouseDownPosition;
TotalTransform.inverse_transform(ref mousePostScale);
unscaledRenderOffset += (mousePostScale - mousePreScale);
scaleChanged?.Invoke(unscaledRenderOffset, layerScale);
break;
case ETransformState.Move:
ScalingTransform.inverse_transform(ref mouseDelta);
unscaledRenderOffset += mouseDelta;
scaleChanged?.Invoke(unscaledRenderOffset, layerScale);
break;
case ETransformState.Edit:
default: // also treat everything else like an edit
break;
}
Invalidate();
} }
public override void OnMouseWheel(MouseEventArgs mouseEvent) public override void OnMouseWheel(MouseEventArgs mouseEvent)

View file

@ -31,7 +31,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MatterHackers.Agg; using MatterHackers.Agg;
@ -59,8 +58,7 @@ namespace MatterHackers.MatterControl.DesignTools
Name = "Radial Pinch".Localize(); Name = "Radial Pinch".Localize();
} }
[PathEditorFactory.TopAndBottomMoveXOnly] [PathEditorFactory.ShowAxis]
[PathEditorFactory.XMustBeGreaterThan0]
public PathEditorFactory.EditableVertexStorage PathForHorizontalOffsets { get; set; } = new PathEditorFactory.EditableVertexStorage(); public PathEditorFactory.EditableVertexStorage PathForHorizontalOffsets { get; set; } = new PathEditorFactory.EditableVertexStorage();
[Description("Specifies the number of vertical cuts required to ensure the part can be pinched well.")] [Description("Specifies the number of vertical cuts required to ensure the part can be pinched well.")]
@ -117,14 +115,6 @@ namespace MatterHackers.MatterControl.DesignTools
return AxisAlignedBoundingBox.CenteredBox(new Vector3(1, 1, sourceAabb.ZSize), center).NewTransformed(this.WorldMatrix()); return AxisAlignedBoundingBox.CenteredBox(new Vector3(1, 1, sourceAabb.ZSize), center).NewTransformed(this.WorldMatrix());
} }
public Vector2 Point1 { get; set; } = new Vector2(0, 0);
public Vector2 Point2 {get; set;} = new Vector2(1, 4);
public Vector2 Point3 { get; set;} = new Vector2(2, 8);
public Vector2 Point4 { get; set;} = new Vector2(3, 12);
public Vector2 Point5 { get; set;} = new Vector2(4, 14);
public Vector2 Point6 { get; set;} = new Vector2(5, 16);
public Vector2 Point7 { get; set;} = new Vector2(6, 18);
public override Task Rebuild() public override Task Rebuild()
{ {
this.DebugDepth("Rebuild"); this.DebugDepth("Rebuild");
@ -174,13 +164,22 @@ namespace MatterHackers.MatterControl.DesignTools
var maxRadius = enclosingCircle.Radius + RotationOffset.Length; var maxRadius = enclosingCircle.Radius + RotationOffset.Length;
//if (PathForHorizontalOffsets.Count == 0) // if there is no path make a bad one
if (PathForHorizontalOffsets.Count == 0)
{ {
var bottomPoint = new Vector2(maxRadius, bottom * 10); var bottomPoint = new Vector2(maxRadius, bottom * 10);
var topPoint = new Vector2(maxRadius, top * 10); var topPoint = new Vector2(maxRadius, top * 10);
var middlePoint = (bottomPoint + topPoint) / 2; var middlePoint = (bottomPoint + topPoint) / 2;
middlePoint.X *= 2; middlePoint.X *= 2;
var Point1 = new Vector2(0, 0);
var Point2 = new Vector2(1, 4);
var Point3 = new Vector2(2, 8);
var Point4 = new Vector2(3, 12);
var Point5 = new Vector2(4, 14);
var Point6 = new Vector2(5, 16);
var Point7 = new Vector2(6, 18);
PathForHorizontalOffsets.Clear(); PathForHorizontalOffsets.Clear();
PathForHorizontalOffsets.MoveTo(Point1); PathForHorizontalOffsets.MoveTo(Point1);
PathForHorizontalOffsets.Curve4(Point2, Point3, Point4); PathForHorizontalOffsets.Curve4(Point2, Point3, Point4);

View file

@ -28,9 +28,7 @@ either expressed or implied, of the FreeBSD Project.
*/ */
using System.Linq; using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.UI; using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.VectorMath; using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.SlicerConfiguration namespace MatterHackers.MatterControl.SlicerConfiguration
@ -82,10 +80,11 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
xEditWidget.ActuallNumberEdit.EditComplete += (sender, e) => xEditWidget.ActuallNumberEdit.EditComplete += (sender, e) =>
{ {
this.SetValue( this.SetValue(
string.Format("{0},{1}", xEditWidget.ActuallNumberEdit.Value.ToString("0.###"), yEditWidget.ActuallNumberEdit.Value.ToString("0.###")), string.Format("{0},{1}",
userInitiated: true); xEditWidget.ActuallNumberEdit.Value.ToString("0.###"),
yEditWidget.ActuallNumberEdit.Value.ToString("0.###")),
userInitiated: true);
}; };
xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown; xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
container.AddChild(xEditWidget); container.AddChild(xEditWidget);
@ -101,10 +100,11 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
yEditWidget.ActuallNumberEdit.EditComplete += (sender, e) => yEditWidget.ActuallNumberEdit.EditComplete += (sender, e) =>
{ {
this.SetValue( this.SetValue(
string.Format("{0},{1}", xEditWidget.ActuallNumberEdit.Value.ToString("0.###"), yEditWidget.ActuallNumberEdit.Value.ToString("0.###")), string.Format("{0},{1}",
xEditWidget.ActuallNumberEdit.Value.ToString("0.###"),
yEditWidget.ActuallNumberEdit.Value.ToString("0.###")),
userInitiated: true); userInitiated: true);
}; };
yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown; yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
container.AddChild(yEditWidget); container.AddChild(yEditWidget);

View file

@ -88,8 +88,9 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
zEditWidget.ActuallNumberEdit.Value.ToString("0.###")), zEditWidget.ActuallNumberEdit.Value.ToString("0.###")),
userInitiated: true); userInitiated: true);
}; };
xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
container.AddChild(xEditWidget); container.AddChild(xEditWidget);
double.TryParse(xyzStrings[1], out double currentYValue); double.TryParse(xyzStrings[1], out double currentYValue);
@ -110,8 +111,9 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
zEditWidget.ActuallNumberEdit.Value.ToString("0.###")), zEditWidget.ActuallNumberEdit.Value.ToString("0.###")),
userInitiated: true); userInitiated: true);
}; };
yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
container.AddChild(yEditWidget); container.AddChild(yEditWidget);
double.TryParse(xyzStrings[2], out double currentZValue); double.TryParse(xyzStrings[2], out double currentZValue);
@ -132,8 +134,9 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
zEditWidget.ActuallNumberEdit.Value.ToString("0.###")), zEditWidget.ActuallNumberEdit.Value.ToString("0.###")),
userInitiated: true); userInitiated: true);
}; };
zEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
container.AddChild(zEditWidget); container.AddChild(zEditWidget);
this.Content = container; this.Content = container;
} }

@ -1 +1 @@
Subproject commit 4329e51c1d2a762a42176e1a1aac9bac43f02da6 Subproject commit 3c76fcb1852d66078ab527e5871dd8c62865680b