diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index 98c3b55a4..a06ac5a0f 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -300,9 +300,74 @@ namespace MatterHackers.MatterControl private List registeredSceneOperations = new List() { + new SceneSelectionOperation() { - () => "Make Support".Localize(), - (scene) => + TitleResolver = () => "Group".Localize(), + Action = (scene) => scene.GroupSelection(), + IsEnabled = (scene) => scene.HasSelection + && scene.SelectedItem is SelectionGroup + && scene.SelectedItem.Children.Count > 1, + Icon = AggContext.StaticData.LoadIcon("group.png").SetPreMultiply(), + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Ungroup".Localize(), + Action = (scene) => scene.UngroupSelection(), + IsEnabled = (scene) => scene.HasSelection, + Icon = AggContext.StaticData.LoadIcon("ungroup.png").SetPreMultiply(), + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Duplicate".Localize(), + Action = (scene) => scene.DuplicateSelection(), + IsEnabled = (scene) => scene.HasSelection, + Icon = AggContext.StaticData.LoadIcon("duplicate.png").SetPreMultiply(), + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Remove".Localize(), + Action = (scene) => scene.DeleteSelection(), + IsEnabled = (scene) => scene.HasSelection, + Icon = AggContext.StaticData.LoadIcon("remove.png").SetPreMultiply(), + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Lay Flat".Localize(), + Action = (scene) => + { + if (scene.HasSelection) + { + scene.MakeLowestFaceFlat(scene.SelectedItem, scene.RootItem); + } + }, + IsEnabled = (scene) => scene.HasSelection, + Icon = AggContext.StaticData.LoadIcon("lay_flat.png").SetPreMultiply(), + }, + + new SceneSelectionOperation() + { + TitleResolver = () => "Subtract".Localize(), + Action = (scene) => DoOpperation(scene, nameof(SubtractEditor), "Subtract"), + Icon = AggContext.StaticData.LoadIcon("subtract.png").SetPreMultiply(), + + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Intersect".Localize(), + Action = (scene) => DoOpperation(scene, nameof(IntersectionEditor), "Intersect"), + Icon = AggContext.StaticData.LoadIcon("intersect.png") + }, +#if DEBUG // keep this work in progress to the editor for now + new SceneSelectionOperation() + { + TitleResolver = () => "Paint Material".Localize(), + Action = (scene) => DoOpperation(scene, nameof(PaintMaterialEditor), "Material Paint"), + Icon = AggContext.StaticData.LoadIcon("paint.png").SetPreMultiply(), + }, + new SceneSelectionOperation() + { + TitleResolver = () => "Make Support".Localize(), + Action = (scene) => { if (scene.SelectedItem != null && scene.SelectedItem.OutputType != PrintOutputTypes.Support) @@ -311,31 +376,21 @@ namespace MatterHackers.MatterControl } } }, + new SceneSelectionOperation() { - () => "Subtract".Localize(), - (scene) => DoOpperation(scene, nameof(SubtractEditor), "Subtract") + TitleResolver = () => "Bend".Localize(), + Action = (scene) => new BendOperation(scene.SelectedItem), }, + new SceneSelectionOperation() { - () => "Intersect".Localize(), - (scene) => DoOpperation(scene, nameof(IntersectionEditor), "Intersect") - }, -#if DEBUG // keep this work in progress to the editor for now - { - () => "Paint Material".Localize(), - (scene) => DoOpperation(scene, nameof(PaintMaterialEditor), "Material Paint") - }, - { - () => "Bend".Localize(), - (scene) => new BendOperation(scene.SelectedItem) - }, - { - () => "Cut Out".Localize(), - (scene) => Console.WriteLine("Cut out") + TitleResolver = () => "Cut Out".Localize(), + Action = (scene) => Console.WriteLine("Cut out") }, + new SceneSelectionOperation() { // Should be a pinch command that makes a pinch object with the correct controls - () => "Pinch".Localize(), - (scene) => scene.UndoBuffer.AddAndDo(new GroupCommand(scene, scene.SelectedItem)) + TitleResolver = () => "Pinch".Localize(), + Action = (scene) => scene.UndoBuffer.AddAndDo(new GroupCommand(scene, scene.SelectedItem)) } #endif }; @@ -1105,10 +1160,7 @@ namespace MatterHackers.MatterControl return Enumerable.Empty(); } - public IEnumerable RegisteredSceneOperations() - { - return registeredSceneOperations; - } + public IEnumerable RegisteredSceneOperations => registeredSceneOperations; public event EventHandler AddPrintersTabRightElement; diff --git a/CustomWidgets/NamedAction.cs b/CustomWidgets/NamedAction.cs index 61e44de2f..433d85adf 100644 --- a/CustomWidgets/NamedAction.cs +++ b/CustomWidgets/NamedAction.cs @@ -32,7 +32,6 @@ using System; using System.Collections.Generic; using MatterHackers.Agg.Image; using MatterHackers.DataConverters3D; -using MatterHackers.MeshVisualizer; namespace MatterHackers.Agg.UI { @@ -54,11 +53,13 @@ namespace MatterHackers.Agg.UI { public Func TitleResolver { get; set; } public string Title => this.TitleResolver?.Invoke(); + public ImageBuffer Icon { get; set; } } public class SceneSelectionOperation : LocalizedAction { public Action Action { get; set; } + public Func IsEnabled { get; set; } } public static class NamedActionExtensions diff --git a/Library/ExtensionMethods.cs b/Library/ExtensionMethods.cs index 736bd29d7..313fe194e 100644 --- a/Library/ExtensionMethods.cs +++ b/Library/ExtensionMethods.cs @@ -34,7 +34,6 @@ using MatterHackers.Agg.Image; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.ImageProcessing; -using MatterHackers.MatterControl.PrintQueue; namespace MatterHackers.MatterControl.Library { @@ -64,13 +63,20 @@ namespace MatterHackers.MatterControl.Library var contentProvider = ApplicationController.Instance.Library.GetContentProvider(item) as ISceneContentProvider; return contentProvider?.CreateItem(item, reporter); } - + // Color ExtensionMethods public static ImageBuffer MultiplyWithPrimaryAccent(this ImageBuffer sourceImage) { return sourceImage.Multiply(ActiveTheme.Instance.PrimaryAccentColor); } + public static ImageBuffer SetPreMultiply(this ImageBuffer sourceImage) + { + sourceImage.SetRecieveBlender(new BlenderPreMultBGRA()); + + return sourceImage; + } + public static ImageBuffer AlphaToPrimaryAccent(this ImageBuffer sourceImage) { return sourceImage.AnyAlphaToColor(ActiveTheme.Instance.PrimaryAccentColor); diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index 3f32a6c7f..e30504590 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -56,7 +56,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private View3DWidget view3DWidget; private InteractiveScene scene; private PrinterConfig printer; - private List sceneActions; private Dictionary> objectEditorsByType; private ObservableCollection materialButtons = new ObservableCollection(); private SectionWidget editorSection; @@ -75,61 +74,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.scene = scene; this.printer = printer; - sceneActions = new List - { - new NamedAction() - { - Title = "Ungroup".Localize(), - Action = () => - { - scene.UngroupSelection(); - }, - IsEnabled = () => scene.HasSelection - }, - new NamedAction() - { - Title = "Group".Localize(), - Action = () => - { - scene.GroupSelection(); - }, - IsEnabled = () => scene.HasSelection - && scene.SelectedItem is SelectionGroup - && scene.SelectedItem.Children.Count > 1 - }, - - new NamedAction() - { - Title = "Lay Flat".Localize(), - Action = () => - { - if (scene.HasSelection) - { - MakeLowestFaceFlat(scene.SelectedItem, scene.RootItem); - } - }, - IsEnabled = () => scene.HasSelection - }, - new NamedAction() - { - Title = "Duplicate".Localize(), - Action = () => - { - scene.DuplicateSelection(); - }, - IsEnabled = () => scene.HasSelection - }, - new NamedAction() - { - Title = "Remove".Localize(), - Action = () => - { - scene.DeleteSelection(); - }, - IsEnabled = () => scene.HasSelection - } - }; - var firstPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) { Padding = 10, @@ -218,27 +162,30 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var operationsContainer = new FlowLeftRightWithWrapping(); - foreach (var sceneAction in sceneActions) + foreach (var namedAction in ApplicationController.Instance.RegisteredSceneOperations) { - var button = new TextButton(sceneAction.Title, theme) - { - Name = sceneAction.Title + " Button", - Margin = theme.ButtonSpacing, - BackgroundColor = theme.MinimalShade - }; - button.Click += (s, e) => sceneAction.Action.Invoke(); + GuiWidget button; - operationsContainer.AddChild(button); - } - - foreach (var namedAction in ApplicationController.Instance.RegisteredSceneOperations()) - { - var button = new TextButton(namedAction.Title, theme) + if (namedAction.Icon != null) { - Name = namedAction.Title + " Button", - Margin = theme.ButtonSpacing, - BackgroundColor = theme.MinimalShade - }; + button = new IconButton(namedAction.Icon, theme) + { + Name = namedAction.Title + " Button", + ToolTipText = namedAction.Title, + Margin = theme.ButtonSpacing, + BackgroundColor = theme.MinimalShade + }; + } + else + { + button = new TextButton(namedAction.Title, theme) + { + Name = namedAction.Title + " Button", + Margin = theme.ButtonSpacing, + BackgroundColor = theme.MinimalShade + }; + } + button.Click += (s, e) => { namedAction.Action.Invoke(scene); @@ -492,104 +439,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return widget; } - private void MakeLowestFaceFlat(IObject3D objectToLayFlatGroup, IObject3D root) - { - bool firstVertex = true; - - IObject3D objectToLayFlat = objectToLayFlatGroup; - - IVertex lowestVertex = null; - Vector3 lowestVertexPosition = Vector3.Zero; - IObject3D itemToLayFlat = null; - - // Process each child, checking for the lowest vertex - var objectsToCheck = objectToLayFlat.VisibleMeshes(); - foreach (var itemToCheck in objectsToCheck) - { - // find the lowest point on the model - for (int testIndex = 0; testIndex < itemToCheck.Mesh.Vertices.Count; testIndex++) - { - var vertex = itemToCheck.Mesh.Vertices[testIndex]; - Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToCheck.WorldMatrix(root)); - if (firstVertex) - { - lowestVertex = itemToCheck.Mesh.Vertices[testIndex]; - lowestVertexPosition = vertexPosition; - itemToLayFlat = itemToCheck; - firstVertex = false; - } - else if (vertexPosition.Z < lowestVertexPosition.Z) - { - lowestVertex = itemToCheck.Mesh.Vertices[testIndex]; - lowestVertexPosition = vertexPosition; - itemToLayFlat = itemToCheck; - } - } - } - - if (lowestVertex == null) - { - // didn't find any selected mesh - return; - } - - Face faceToLayFlat = null; - double lowestAngleOfAnyFace = double.MaxValue; - // Check all the faces that are connected to the lowest point to find out which one to lay flat. - foreach (Face face in lowestVertex.ConnectedFaces()) - { - double biggestAngleToFaceVertex = double.MinValue; - foreach (IVertex faceVertex in face.Vertices()) - { - if (faceVertex != lowestVertex) - { - Vector3 faceVertexPosition = Vector3.Transform(faceVertex.Position, itemToLayFlat.WorldMatrix(root)); - Vector3 pointRelLowest = faceVertexPosition - lowestVertexPosition; - double xLeg = new Vector2(pointRelLowest.X, pointRelLowest.Y).Length; - double yLeg = pointRelLowest.Z; - double angle = Math.Atan2(yLeg, xLeg); - if (angle > biggestAngleToFaceVertex) - { - biggestAngleToFaceVertex = angle; - } - } - } - if (biggestAngleToFaceVertex < lowestAngleOfAnyFace) - { - lowestAngleOfAnyFace = biggestAngleToFaceVertex; - faceToLayFlat = face; - } - } - - double maxDistFromLowestZ = 0; - List faceVertices = new List(); - foreach (IVertex vertex in faceToLayFlat.Vertices()) - { - Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToLayFlat.WorldMatrix(root)); - faceVertices.Add(vertexPosition); - maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestVertexPosition.Z); - } - - if (maxDistFromLowestZ > .001) - { - Vector3 xPositive = (faceVertices[1] - faceVertices[0]).GetNormal(); - Vector3 yPositive = (faceVertices[2] - faceVertices[0]).GetNormal(); - Vector3 planeNormal = Vector3.Cross(xPositive, yPositive).GetNormal(); - - // this code takes the minimum rotation required and looks much better. - Quaternion rotation = new Quaternion(planeNormal, new Vector3(0, 0, -1)); - Matrix4X4 partLevelMatrix = Matrix4X4.CreateRotation(rotation); - - // rotate it - objectToLayFlatGroup.Matrix = PlatingHelper.ApplyAtCenter(objectToLayFlatGroup, partLevelMatrix); - - scene.Invalidate(); - Invalidate(); - } - - PlatingHelper.PlaceOnBed(objectToLayFlatGroup); - } - private List GetTransforms(int axisIndex, AxisAlignment alignment) { var transformDatas = new List(); diff --git a/PartPreviewWindow/View3D/SceneActions.cs b/PartPreviewWindow/View3D/SceneActions.cs index 6a50c5571..639b0ecf7 100644 --- a/PartPreviewWindow/View3D/SceneActions.cs +++ b/PartPreviewWindow/View3D/SceneActions.cs @@ -27,6 +27,7 @@ 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.Linq; using System.Threading; @@ -34,6 +35,7 @@ using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; +using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow @@ -212,6 +214,102 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } + public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup, IObject3D root) + { + bool firstVertex = true; + + IObject3D objectToLayFlat = objectToLayFlatGroup; + + IVertex lowestVertex = null; + Vector3 lowestVertexPosition = Vector3.Zero; + IObject3D itemToLayFlat = null; + + // Process each child, checking for the lowest vertex + foreach (var itemToCheck in objectToLayFlat.VisibleMeshes()) + { + // find the lowest point on the model + for (int testIndex = 0; testIndex < itemToCheck.Mesh.Vertices.Count; testIndex++) + { + var vertex = itemToCheck.Mesh.Vertices[testIndex]; + Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToCheck.WorldMatrix(root)); + if (firstVertex) + { + lowestVertex = itemToCheck.Mesh.Vertices[testIndex]; + lowestVertexPosition = vertexPosition; + itemToLayFlat = itemToCheck; + firstVertex = false; + } + else if (vertexPosition.Z < lowestVertexPosition.Z) + { + lowestVertex = itemToCheck.Mesh.Vertices[testIndex]; + lowestVertexPosition = vertexPosition; + itemToLayFlat = itemToCheck; + } + } + } + + if (lowestVertex == null) + { + // didn't find any selected mesh + return; + } + + Face faceToLayFlat = null; + double lowestAngleOfAnyFace = double.MaxValue; + // Check all the faces that are connected to the lowest point to find out which one to lay flat. + foreach (Face face in lowestVertex.ConnectedFaces()) + { + double biggestAngleToFaceVertex = double.MinValue; + foreach (IVertex faceVertex in face.Vertices()) + { + if (faceVertex != lowestVertex) + { + Vector3 faceVertexPosition = Vector3.Transform(faceVertex.Position, itemToLayFlat.WorldMatrix(root)); + Vector3 pointRelLowest = faceVertexPosition - lowestVertexPosition; + double xLeg = new Vector2(pointRelLowest.X, pointRelLowest.Y).Length; + double yLeg = pointRelLowest.Z; + double angle = Math.Atan2(yLeg, xLeg); + if (angle > biggestAngleToFaceVertex) + { + biggestAngleToFaceVertex = angle; + } + } + } + if (biggestAngleToFaceVertex < lowestAngleOfAnyFace) + { + lowestAngleOfAnyFace = biggestAngleToFaceVertex; + faceToLayFlat = face; + } + } + + double maxDistFromLowestZ = 0; + List faceVertices = new List(); + foreach (IVertex vertex in faceToLayFlat.Vertices()) + { + Vector3 vertexPosition = Vector3.Transform(vertex.Position, itemToLayFlat.WorldMatrix(root)); + faceVertices.Add(vertexPosition); + maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestVertexPosition.Z); + } + + if (maxDistFromLowestZ > .001) + { + Vector3 xPositive = (faceVertices[1] - faceVertices[0]).GetNormal(); + Vector3 yPositive = (faceVertices[2] - faceVertices[0]).GetNormal(); + Vector3 planeNormal = Vector3.Cross(xPositive, yPositive).GetNormal(); + + // this code takes the minimum rotation required and looks much better. + Quaternion rotation = new Quaternion(planeNormal, new Vector3(0, 0, -1)); + Matrix4X4 partLevelMatrix = Matrix4X4.CreateRotation(rotation); + + // rotate it + objectToLayFlatGroup.Matrix = PlatingHelper.ApplyAtCenter(objectToLayFlatGroup, partLevelMatrix); + + scene.Invalidate(); + } + + PlatingHelper.PlaceOnBed(objectToLayFlatGroup); + } + internal class ArangeUndoCommand : IUndoRedoCommand { private List allUndoTransforms = new List(); diff --git a/StaticData/Icons/duplicate.png b/StaticData/Icons/duplicate.png new file mode 100644 index 000000000..a8cadee79 Binary files /dev/null and b/StaticData/Icons/duplicate.png differ diff --git a/StaticData/Icons/group.png b/StaticData/Icons/group.png new file mode 100644 index 000000000..0c15bdf9b Binary files /dev/null and b/StaticData/Icons/group.png differ diff --git a/StaticData/Icons/intersect.png b/StaticData/Icons/intersect.png new file mode 100644 index 000000000..bc468f6ed Binary files /dev/null and b/StaticData/Icons/intersect.png differ diff --git a/StaticData/Icons/lay_flat.png b/StaticData/Icons/lay_flat.png new file mode 100644 index 000000000..7ede537c5 Binary files /dev/null and b/StaticData/Icons/lay_flat.png differ diff --git a/StaticData/Icons/paint.png b/StaticData/Icons/paint.png new file mode 100644 index 000000000..e9adbc1f0 Binary files /dev/null and b/StaticData/Icons/paint.png differ diff --git a/StaticData/Icons/remove.png b/StaticData/Icons/remove.png new file mode 100644 index 000000000..89b486293 Binary files /dev/null and b/StaticData/Icons/remove.png differ diff --git a/StaticData/Icons/subtract.png b/StaticData/Icons/subtract.png new file mode 100644 index 000000000..d8eeb5e37 Binary files /dev/null and b/StaticData/Icons/subtract.png differ diff --git a/StaticData/Icons/ungroup.png b/StaticData/Icons/ungroup.png new file mode 100644 index 000000000..3464c7f2f Binary files /dev/null and b/StaticData/Icons/ungroup.png differ