Improving path objects

This commit is contained in:
Lars Brubaker 2022-08-12 18:05:37 -07:00
parent f5c8b2008c
commit 6e3c4be07c
13 changed files with 51 additions and 357 deletions

View file

@ -529,60 +529,11 @@ namespace MatterHackers.MatterControl
ReportStartupProgress(0, "ShowAsSystemWindow");
AddTextWidgetRightClickMenu();
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
return rootSystemWindow;
}
public static void AddTextWidgetRightClickMenu()
{
InternalTextEditWidget.DefaultRightClick += (s, e) =>
{
var textEditWidget = s as InternalTextEditWidget;
var theme = ApplicationController.Instance.MenuTheme;
var popupMenu = new PopupMenu(theme);
var cut = popupMenu.CreateMenuItem("Cut".Localize());
cut.Enabled = !string.IsNullOrEmpty(s.Selection);
cut.Click += (s2, e2) =>
{
textEditWidget?.CopySelection();
textEditWidget?.DeleteSelection();
};
var copy = popupMenu.CreateMenuItem("Copy".Localize());
copy.Enabled = !string.IsNullOrEmpty(s.Selection);
copy.Click += (s2, e2) =>
{
textEditWidget?.CopySelection();
};
var paste = popupMenu.CreateMenuItem("Paste".Localize());
paste.Enabled = Clipboard.Instance.ContainsText;
paste.Click += (s2, e2) =>
{
textEditWidget?.PasteFromClipboard();
};
popupMenu.CreateSeparator();
var selectAll = popupMenu.CreateMenuItem("Select All".Localize());
selectAll.Enabled = !string.IsNullOrEmpty(textEditWidget.Text);
selectAll.Click += (s2, e2) =>
{
textEditWidget?.SelectAll();
};
textEditWidget.KeepMenuOpen = true;
popupMenu.Closed += (s3, e3) =>
{
textEditWidget.KeepMenuOpen = false;
};
popupMenu.ShowMenu(s, e);
};
}
private static void SystemWindow_KeyPressed(object sender, KeyPressEventArgs keyEvent)
{
if (sender is SystemWindow systemWindow)

View file

@ -894,7 +894,7 @@ namespace MatterHackers.MatterControl
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("InflatePath"), SceneOperations.ById("OutlinePath")
});
PrimaryOperations.Add(typeof(TextPathObject3D), new List<SceneOperation>
PrimaryOperations.Add(typeof(TextObject3D), new List<SceneOperation>
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("InflatePath"), SceneOperations.ById("OutlinePath")
});

View file

@ -28,34 +28,15 @@ either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.PolygonMesh.Processors;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace MatterHackers.MatterControl.DesignTools
{
public class PathObject3D : Object3D, IEditorDraw
{
[JsonIgnore]
private IVertexSource _vertexSource = new VertexStorage();
public override IVertexSource VertexSource
{
get => _vertexSource;
set
{
_vertexSource = value;
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
}
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
this.DrawPath();

View file

@ -27,7 +27,6 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -65,21 +64,6 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
public override bool CanApply => true;
[JsonIgnore]
private IVertexSource VertexSource
{
get
{
var item = this.Descendants().Where(c => c.VertexSource != null).FirstOrDefault();
if (item != null)
{
return item.VertexSource;
}
return null;
}
}
public override void Apply(UndoBuffer undoBuffer)
{
if (Mesh == null)
@ -131,11 +115,11 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
}
(Vector3, Vector3) GetStartEnd(IObject3D pathObject)
(Vector3, Vector3) GetStartEnd(IObject3D pathObject, IVertexSource path)
{
// draw the line that is the rotation point
var aabb = this.GetAxisAlignedBoundingBox();
var vertexSource = this.VertexSource.Transform(Matrix);
var vertexSource = path.Transform(Matrix);
var bounds = vertexSource.GetBounds();
var lineX = bounds.Left + AxisPosition.Value(this);
@ -146,10 +130,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
var child = this.Children.FirstOrDefault();
if (child?.VertexSource != null)
var path = this.CombinedVisibleChildrenPaths();
if (path == null)
{
var (start, end) = GetStartEnd(child);
var (start, end) = GetStartEnd(this, path);
layer.World.Render3DLine(start, end, Color.Red, true);
layer.World.Render3DLine(start, end, Color.Red.WithAlpha(20), false);
}
@ -157,14 +141,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
var child = this.Children.FirstOrDefault();
if (child.VertexSource != null)
{
var (start, end) = GetStartEnd(child);
return new AxisAlignedBoundingBox(new Vector3[] { start, end });
}
return AxisAlignedBoundingBox.Empty();
return this.GetWorldspaceAabbOfDrawPath();
}
private CancellationTokenSource cancellationToken;
@ -209,7 +186,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
var vertexSource = this.VertexSource;
var vertexSource = this.CombinedVisibleChildrenPaths();
var pathBounds = vertexSource.GetBounds();
vertexSource = vertexSource.Translate(-pathBounds.Left - axisPosition, 0);
Mesh mesh = VertexSourceToMesh.Revolve(vertexSource,

View file

@ -28,9 +28,7 @@ either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using ClipperLib;
using MatterHackers.Agg.UI;
@ -48,8 +46,6 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
public class SmoothPathObject3D : Object3D, IEditorDraw, IObject3DControlsProvider
{
public override IVertexSource VertexSource { get; set; } = new VertexStorage();
public SmoothPathObject3D()
{
Name = "Smooth Path".Localize();
@ -105,7 +101,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
DoSmoothing((long)(SmoothDistance.Value(this) * 1000), Iterations.Value(this));
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
this.Mesh = VertexStorage.Extrude(Constants.PathPolygonsHeight);
UiThread.RunOnIdle(() =>
{
@ -121,15 +117,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
private void DoSmoothing(long maxDist, int interations)
{
bool closedPath = true;
var path = this.Children.Where(c => c.VertexSource != null).FirstOrDefault();
var path = this.CombinedVisibleChildrenPaths();
if (path == null)
{
// clear our existing data
VertexSource = new VertexStorage();
VertexStorage = new VertexStorage();
return;
}
var sourceVertices = path.VertexSource;
var sourceVertices = path;
var inputPolygons = sourceVertices.CreatePolygons();
@ -180,7 +176,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
outputPolygons = ClipperLib.Clipper.CleanPolygons(outputPolygons, Math.Max(maxDist / 10, 1.415));
}
VertexSource = outputPolygons.CreateVertexStorage();
VertexStorage = outputPolygons.CreateVertexStorage();
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)

View file

@ -78,10 +78,15 @@ namespace MatterHackers.MatterControl.DesignTools
Color = Operations.Object3DExtensions.PrimitiveColors["Text"];
}
public static async Task<TextObject3D> Create()
public static async Task<TextObject3D> Create(bool setTo2D = false)
{
var item = new TextObject3D();
if (!setTo2D)
{
item.Output = OutputDimensions.Output2D;
}
await item.Rebuild();
return item;

View file

@ -1,208 +0,0 @@
/*
Copyright (c) 2018, Lars Brubaker, 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;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using MatterHackers.Agg.Font;
using MatterHackers.Agg.Transform;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.DataConverters3D.UndoCommands;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.PolygonMesh;
using MatterHackers.PolygonMesh.Processors;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace MatterHackers.MatterControl.DesignTools
{
public class TextPathObject3D : Object3D, IEditorDraw
{
public TextPathObject3D()
{
Name = "Text".Localize();
Color = Operations.Object3DExtensions.PrimitiveColors["Text"];
}
public static async Task<TextPathObject3D> Create()
{
var item = new TextPathObject3D();
await item.Rebuild();
return item;
}
[DisplayName("Text")]
public StringOrExpression Text { get; set; } = "Text";
public DoubleOrExpression PointSize { get; set; } = 24;
[Sortable]
[JsonConverter(typeof(StringEnumConverter))]
public NamedTypeFace Font { get; set; } = new NamedTypeFace();
public override bool CanApply => true;
public override IVertexSource VertexSource { get; set; } = new VertexStorage();
public override void Apply(UndoBuffer undoBuffer)
{
// change this from a text object to a group
var newContainer = new GroupObject3D();
newContainer.CopyProperties(this, Object3DPropertyFlags.All);
foreach (var child in this.Children)
{
newContainer.Children.Add(child.Clone());
}
undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { newContainer }));
}
public override async void OnInvalidate(InvalidateArgs invalidateArgs)
{
if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateArgs.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this))
{
await Rebuild();
}
else if (SheetObject3D.NeedsRebuild(this, invalidateArgs))
{
await Rebuild();
}
else
{
base.OnInvalidate(invalidateArgs);
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
using (RebuildLock())
{
double pointsToMm = 0.352778;
var printer = new TypeFacePrinter(Text.Value(this), new StyledTypeFace(ApplicationController.GetTypeFace(Font), PointSize.Value(this)))
{
ResolutionScale = 10
};
var scaledLetterPrinter = new VertexSourceApplyTransform(printer, Affine.NewScaling(pointsToMm));
var vertexSource = new VertexStorage();
foreach (var vertex in scaledLetterPrinter.Vertices())
{
if (vertex.IsMoveTo)
{
vertexSource.MoveTo(vertex.position);
}
else if (vertex.IsLineTo)
{
vertexSource.LineTo(vertex.position);
}
else if (vertex.IsClose)
{
vertexSource.ClosePolygon();
}
}
vertexSource = (VertexStorage)vertexSource.Union(vertexSource);
this.VertexSource = vertexSource;
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
}
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path));
return Task.CompletedTask;
}
public override Mesh Mesh
{
get
{
if (base.Mesh == null)
{
using (this.RebuildLock())
{
var bounds = this.VertexSource.GetBounds();
if (bounds.Width == 0 || bounds.Height == 0)
{
bounds = new Agg.RectangleDouble(0, 0, 60, 20);
}
var center = bounds.Center;
var mesh = PlatonicSolids.CreateCube(Math.Max(8, bounds.Width), Math.Max(8, bounds.Height), 0.2);
mesh.Translate(new Vector3(center.X, center.Y, 0));
base.Mesh = mesh;
}
}
return base.Mesh;
}
}
private Mesh InitMesh()
{
if (!string.IsNullOrWhiteSpace(Text.Value(this)))
{
}
return null;
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
this.DrawPath();
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return this.GetWorldspaceAabbOfDrawPath();
}
}
}

View file

@ -96,10 +96,6 @@ namespace MatterHackers.MatterControl.Library
"Calibration Face".Localize(),
async () => await XyCalibrationFaceObject3D.Create())
{ DateCreated = new System.DateTime(index++) },
new GeneratorItem(
"Text2".Localize(),
async () => await TextPathObject3D.Create())
{ DateCreated = new System.DateTime(index++) },
new GeneratorItem(
"Path".Localize(),
() =>
@ -112,7 +108,7 @@ namespace MatterHackers.MatterControl.Library
var path = new PathObject3D()
{
VertexSource = storage
VertexStorage = storage
};
return Task.FromResult<IObject3D>(path);

View file

@ -81,7 +81,7 @@ namespace MatterHackers.MatterControl.Library
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextObject3D.Create())
async () => await TextObject3D.Create(true))
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Cylinder".Localize(),
@ -218,7 +218,7 @@ namespace MatterHackers.MatterControl.Library
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Text".Localize(),
async () => await TextPathObject3D.Create())
async () => await TextObject3D.Create())
{ DateCreated = new DateTime(index++) },
new GeneratorItem(
"Oval".Localize(),

@ -1 +1 @@
Subproject commit 36ca84b13297ae1521c8493a09f35140834018fd
Subproject commit d061bb620e8fa595aa2d2509dc33bb7874140278

View file

@ -27,44 +27,40 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.UI.Tests;
using MatterHackers.GuiAutomation;
using NUnit.Framework;
using System.Threading.Tasks;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class CreateLibraryFolder
{
[Test, ChildProcessTest]
public async Task CreateFolderStartsWithTextFieldFocusedAndEditable()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
[TestFixture, Category("MatterControl.UI.Automation")]
public class CreateLibraryFolder
{
[Test, ChildProcessTest]
public async Task CreateFolderStartsWithTextFieldFocusedAndEditable()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.NavigateToFolder("Local Library Row Item Collection");
testRunner.InvokeLibraryCreateFolderDialog();
testRunner.NavigateToFolder("Local Library Row Item Collection");
testRunner.InvokeLibraryCreateFolderDialog();
testRunner.Delay(.5);
testRunner.Type("Test Text");
testRunner.Delay(.5);
testRunner.Delay(.5);
testRunner.Type("Test Text");
testRunner.Delay(.5);
var textWidgetMH = testRunner.GetWidgetByName("InputBoxPage TextEditWidget", out _) as ThemedTextEditWidget;
var textWidgetMH = testRunner.GetWidgetByName("InputBoxPage TextEditWidget", out _) as ThemedTextEditWidget;
Assert.IsTrue(textWidgetMH != null, "Found Text Widget");
Assert.IsTrue(textWidgetMH.Text == "Test Text", "Had the right text");
Assert.IsTrue(textWidgetMH != null, "Found Text Widget");
Assert.IsTrue(textWidgetMH.Text == "Test Text", "Had the right text");
testRunner.ClickByName("Cancel Wizard Button");
testRunner.Delay(.5);
testRunner.ClickByName("Cancel Wizard Button");
testRunner.Delay(.5);
return Task.CompletedTask;
});
}
}
return Task.CompletedTask;
});
}
}
}

View file

@ -34,7 +34,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
Name = "Main Window",
};
Application.AddTextWidgetRightClickMenu();
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;
@ -189,7 +189,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
Name = "Main Window",
};
Application.AddTextWidgetRightClickMenu();
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;

View file

@ -358,7 +358,7 @@ namespace MatterControl.Tests.MatterControl
Name = "Main Window",
};
Application.AddTextWidgetRightClickMenu();
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;