From f60259e9d3b2568ffd7b42858bb071ce8f53dcd3 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Mon, 8 Jan 2018 23:34:40 -0800 Subject: [PATCH] Consolidate header and footer bars into SelectedObjectPanel --- Library/Widgets/PrintLibraryWidget.cs | 2 - PartPreviewWindow/PartTabPage.cs | 7 +- PartPreviewWindow/SectionWidget.cs | 32 +- PartPreviewWindow/SelectedObjectPanel.cs | 562 +++++++++++++- PartPreviewWindow/View3D/MeshViewerWidget.cs | 6 +- PartPreviewWindow/View3D/SceneActions.cs | 37 +- .../View3D/SideBar/ScaleControl.cs | 5 +- PartPreviewWindow/View3D/View3DWidget.cs | 715 +----------------- PartPreviewWindow/ViewControls3D.cs | 229 +++++- .../PartPreviewTests.cs | 16 +- .../SqLiteLibraryProvider.cs | 4 +- 11 files changed, 788 insertions(+), 827 deletions(-) diff --git a/Library/Widgets/PrintLibraryWidget.cs b/Library/Widgets/PrintLibraryWidget.cs index 28ea71c1a..54d057a9a 100644 --- a/Library/Widgets/PrintLibraryWidget.cs +++ b/Library/Widgets/PrintLibraryWidget.cs @@ -34,14 +34,12 @@ using System.Linq; using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; -using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.PrinterCommunication; using MatterHackers.MatterControl.PrintQueue; -using System.Threading; namespace MatterHackers.MatterControl.PrintLibrary { diff --git a/PartPreviewWindow/PartTabPage.cs b/PartPreviewWindow/PartTabPage.cs index 67ab973f5..028656661 100644 --- a/PartPreviewWindow/PartTabPage.cs +++ b/PartPreviewWindow/PartTabPage.cs @@ -55,12 +55,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.BackgroundColor = theme.TabBodyBackground; this.Padding = 0; + bool isPrinterType = this is PrinterTabPage; + viewControls3D = new ViewControls3D(sceneContext, theme, sceneContext.Scene.UndoBuffer) { //BackgroundColor = new Color(0, 0, 0, theme.OverlayAlpha), VAnchor = VAnchor.Top | VAnchor.Fit, HAnchor = HAnchor.Left | HAnchor.Stretch, Visible = true, + IsPrinterMode = isPrinterType }; viewControls3D.ResetView += (sender, e) => { @@ -73,8 +76,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow viewControls3D.OverflowMenu.BackgroundColor = theme.ResolveColor(theme.TabBodyBackground, theme.TabBodyBackground); viewControls3D.OverflowMenu.Name = "View3D Overflow Menu"; - bool isPrinterType = this is PrinterTabPage; - // The 3D model view view3DWidget = new View3DWidget( printer, @@ -85,6 +86,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this, editorType: (isPrinterType) ? MeshViewerWidget.EditorType.Printer : MeshViewerWidget.EditorType.Part); + viewControls3D.SetView3DWidget(view3DWidget); + this.AddChild(topToBottom = new FlowLayoutWidget(FlowDirection.TopToBottom) { HAnchor = HAnchor.Stretch, diff --git a/PartPreviewWindow/SectionWidget.cs b/PartPreviewWindow/SectionWidget.cs index 90412095d..7d2a200cb 100644 --- a/PartPreviewWindow/SectionWidget.cs +++ b/PartPreviewWindow/SectionWidget.cs @@ -5,7 +5,9 @@ namespace MatterHackers.MatterControl.CustomWidgets { public class SectionWidget : FlowLayoutWidget { - public SectionWidget(string sectionTitle, Color textColor, GuiWidget sectionContent, GuiWidget rightAlignedContent = null, int headingPointSize = -1, bool expandingContent = true) + private GuiWidget contentWidget; + + public SectionWidget(string sectionTitle, Color textColor, GuiWidget sectionContent, GuiWidget rightAlignedContent = null, int headingPointSize = -1, bool expandingContent = true, bool expanded = true) : base (FlowDirection.TopToBottom) { this.HAnchor = HAnchor.Stretch; @@ -22,11 +24,14 @@ namespace MatterHackers.MatterControl.CustomWidgets if (expandingContent) { - var checkbox = new ExpandCheckboxButton(sectionTitle, pointSize: pointSize); - checkbox.Checked = true; + var checkbox = new ExpandCheckboxButton(sectionTitle, pointSize: pointSize) + { + HAnchor = HAnchor.Stretch, + Checked = expanded + }; checkbox.CheckedStateChanged += (s, e) => { - sectionContent.Visible = checkbox.Checked; + contentWidget.Visible = checkbox.Checked; }; heading = checkbox; @@ -60,10 +65,21 @@ namespace MatterHackers.MatterControl.CustomWidgets }); } - // Force padding and add content widget - sectionContent.HAnchor = HAnchor.Stretch; - sectionContent.BackgroundColor = ApplicationController.Instance.Theme.MinimalShade; - this.AddChild(sectionContent); + sectionContent.Visible = expanded; + + this.SetContentWidget(sectionContent); + } + + public void SetContentWidget(GuiWidget guiWidget) + { + contentWidget?.Close(); + + contentWidget = guiWidget; + contentWidget.HAnchor = HAnchor.Stretch; + contentWidget.VAnchor = VAnchor.Fit; + contentWidget.BackgroundColor = ApplicationController.Instance.Theme.MinimalShade; + + this.AddChild(contentWidget); } } } \ No newline at end of file diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index 5abcad8c2..ad9a81e00 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -29,6 +29,8 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Threading.Tasks; using MatterHackers.Agg; @@ -36,7 +38,11 @@ using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; +using MatterHackers.MatterControl.DataStorage; using MatterHackers.MatterControl.Library; +using MatterHackers.MeshVisualizer; +using MatterHackers.PolygonMesh; +using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow { @@ -50,22 +56,88 @@ 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; + private TextButton editButton; public SelectedObjectPanel(View3DWidget view3DWidget, InteractiveScene scene, ThemeConfig theme, PrinterConfig printer) : base(FlowDirection.TopToBottom) { this.HAnchor = HAnchor.Stretch; this.VAnchor = VAnchor.Top | VAnchor.Fit; - this.Padding = new BorderDouble(8, 10); - this.MinimumSize = new VectorMath.Vector2(220, 0); + this.Padding = 0; // new BorderDouble(8, 10); this.view3DWidget = view3DWidget; this.theme = theme; this.scene = scene; this.printer = printer; - this.AddChild(itemName = new TextWidget("", textColor: ActiveTheme.Instance.PrimaryTextColor) + 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, + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Fit + }; + this.AddChild(firstPanel); + + firstPanel.AddChild(itemName = new TextWidget("", textColor: ActiveTheme.Instance.PrimaryTextColor) { AutoExpandBoundsToText = true, EllipsisIfClipped = true, @@ -73,7 +145,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); var behavior3DTypeButtons = new FlowLayoutWidget(); - this.AddChild(behavior3DTypeButtons); + firstPanel.AddChild(behavior3DTypeButtons); var buttonMargin = new BorderDouble(2, 5); @@ -128,7 +200,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var scrollable = new ScrollableWidget(true) { Name = "editorPanel", - Margin = new BorderDouble(top: 10), HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Stretch, }; @@ -138,6 +209,80 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.AddChild(scrollable); + // Add heading separator + editorPanel.AddChild(new HorizontalLine(25) + { + Margin = new BorderDouble(0) + }); + + var operationsContainer = new FlowLeftRightWithWrapping(); + + foreach (var sceneAction in sceneActions) + { + var button = new TextButton(sceneAction.Title, theme) + { + Name = sceneAction.Title + " Button", + Margin = theme.ButtonSpacing, + BackgroundColor = theme.MinimalShade + }; + button.Click += (s, e) => sceneAction.Action.Invoke(); + + operationsContainer.AddChild(button); + } + + foreach (var namedAction in ApplicationController.Instance.RegisteredSceneOperations()) + { + var button = new TextButton(namedAction.Title, theme) + { + Name = namedAction.Title + " Button", + Margin = theme.ButtonSpacing, + BackgroundColor = theme.MinimalShade + }; + button.Click += (s, e) => + { + namedAction.Action.Invoke(scene); + }; + operationsContainer.AddChild(button); + } + + + + + var operationsSection = new SectionWidget("Operations".Localize(), ActiveTheme.Instance.PrimaryTextColor, operationsContainer); + editorPanel.AddChild(operationsSection); + + editorSection = new SectionWidget("Editor", ActiveTheme.Instance.PrimaryTextColor, new GuiWidget()); + editorPanel.AddChild(editorSection); + + // TODO: Implements + //alignButton.Enabled = this.scene.HasSelection + // && this.scene.SelectedItem is SelectionGroup + // && this.scene.SelectedItem.Children.Count > 1; + + var alignSection = new SectionWidget("Align".Localize(), ActiveTheme.Instance.PrimaryTextColor, this.AddAlignControls(), expanded: false) + { + Name = "Align Panel", + }; + editorPanel.AddChild(alignSection); + + var mirrorSection = new SectionWidget("Mirror".Localize(), ActiveTheme.Instance.PrimaryTextColor, new MirrorControls(scene), expanded: false) + { + Name = "Mirror Panel", + }; + editorPanel.AddChild(mirrorSection); + + var scaleSection = new SectionWidget("Scale".Localize(), ActiveTheme.Instance.PrimaryTextColor, new ScaleControls(scene, ActiveTheme.Instance.PrimaryTextColor), expanded: false) + { + Name = "Scale Panel", + }; + editorPanel.AddChild(scaleSection); + + var materialsSection = new SectionWidget("Materials".Localize(), ActiveTheme.Instance.PrimaryTextColor, this.AddMaterialControls(), expanded: false) + { + Name = "Materials Panel", + }; + editorPanel.AddChild(materialsSection); + HashSet mappedEditors; objectEditorsByType = new Dictionary>(); @@ -172,7 +317,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.item = selectedItem; - this.editorPanel.RemoveAllChildren(); + //this.editorPanel.RemoveAllChildren(); var viewMode = printer?.ViewState.ViewMode; @@ -204,21 +349,21 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (mappedEditors != null) { - var dropDownList = new DropDownList("", ActiveTheme.Instance.PrimaryTextColor, maxHeight: 300) - { - HAnchor = HAnchor.Stretch - }; + //var dropDownList = new DropDownList("", ActiveTheme.Instance.PrimaryTextColor, maxHeight: 300) + //{ + // HAnchor = HAnchor.Stretch + //}; - foreach (IObject3DEditor editor in mappedEditors) - { - MenuItem menuItem = dropDownList.AddItem(editor.Name); - menuItem.Selected += (s, e2) => - { - ShowObjectEditor(editor); - }; - } + //foreach (IObject3DEditor editor in mappedEditors) + //{ + // MenuItem menuItem = dropDownList.AddItem(editor.Name); + // menuItem.Selected += (s, e2) => + // { + // ShowObjectEditor(editor); + // }; + //} - editorPanel.AddChild(dropDownList); + //editorPanel.AddChild(dropDownList); // Select the active editor or fall back to the first if not found var firstEditor = (from editor in mappedEditors @@ -232,25 +377,41 @@ namespace MatterHackers.MatterControl.PartPreviewWindow firstEditor = mappedEditors.First(); } - int selectedIndex = 0; - for (int i = 0; i < dropDownList.MenuItems.Count; i++) - { - if (dropDownList.MenuItems[i].Text == firstEditor.Name) - { - selectedIndex = i; - break; - } - } + //int selectedIndex = 0; + //for (int i = 0; i < dropDownList.MenuItems.Count; i++) + //{ + // if (dropDownList.MenuItems[i].Text == firstEditor.Name) + // { + // selectedIndex = i; + // break; + // } + //} - dropDownList.SelectedIndex = selectedIndex; + //dropDownList.SelectedIndex = selectedIndex; ShowObjectEditor(firstEditor); + + if (materialButtons?.Count > 0) + { + bool setSelection = false; + // Set the material selector to have the correct material button selected + for (int i = 0; i < materialButtons.Count; i++) + { + if (selectedItem.MaterialIndex == i) + { + ((RadioButton)materialButtons[i]).Checked = true; + setSelection = true; + } + } + + if (!setSelection) + { + ((RadioButton)materialButtons[0]).Checked = true; + } + } } } - private GuiWidget activeEditorWidget; - private TextButton editButton; - private void ShowObjectEditor(IObject3DEditor editor) { if (editor == null) @@ -258,15 +419,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return; } - activeEditorWidget?.Close(); + var editorWidget = editor.Create(scene.SelectedItem, view3DWidget, theme); + editorWidget.HAnchor = HAnchor.Stretch; + editorWidget.VAnchor = VAnchor.Fit; - var newEditor = editor.Create(scene.SelectedItem, view3DWidget, theme); - newEditor.HAnchor = HAnchor.Stretch; - newEditor.VAnchor = VAnchor.Fit; - - editorPanel.AddChild(newEditor); - - activeEditorWidget = newEditor; + editorSection.SetContentWidget(editorWidget); } public void Save(ILibraryItem item, IObject3D content) @@ -278,6 +435,331 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); } + private GuiWidget AddMaterialControls() + { + var widget = new IgnoredPopupWidget() + { + HAnchor = HAnchor.Fit, + VAnchor = VAnchor.Fit, + BackgroundColor = Color.White, + Padding = new BorderDouble(0, 5, 5, 0) + }; + + FlowLayoutWidget buttonPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + VAnchor = VAnchor.Fit, + HAnchor = HAnchor.Fit, + }; + widget.AddChild(buttonPanel); + + materialButtons.Clear(); + int extruderCount = 4; + for (int extruderIndex = 0; extruderIndex < extruderCount; extruderIndex++) + { + FlowLayoutWidget colorSelectionContainer = new FlowLayoutWidget(FlowDirection.LeftToRight) + { + HAnchor = HAnchor.Fit, + Padding = new BorderDouble(5) + }; + buttonPanel.AddChild(colorSelectionContainer); + + string materialLabelText = string.Format("{0} {1}", "Material".Localize(), extruderIndex + 1); + + RadioButton materialSelection = new RadioButton(materialLabelText, textColor: Color.Black); + materialButtons.Add(materialSelection); + materialSelection.SiblingRadioButtonList = materialButtons; + colorSelectionContainer.AddChild(materialSelection); + colorSelectionContainer.AddChild(new HorizontalSpacer()); + int extruderIndexCanPassToClick = extruderIndex; + materialSelection.Click += (sender, e) => + { + if (scene.HasSelection) + { + scene.SelectedItem.MaterialIndex = extruderIndexCanPassToClick; + scene.Invalidate(); + + // "View 3D Overflow Menu" // the menu to click on + // "Materials Option" // the item to highlight + //HelpSystem. + } + }; + + colorSelectionContainer.AddChild(new GuiWidget(16, 16) + { + BackgroundColor = MaterialRendering.Color(extruderIndex), + Margin = new BorderDouble(5, 0, 0, 0) + }); + } + + 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(); + var totalAABB = scene.SelectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity); + + Vector3 firstSourceOrigin = new Vector3(double.MaxValue, double.MaxValue, double.MaxValue); + + // move the objects to the right place + foreach (var child in scene.SelectedItem.Children) + { + var childAABB = child.GetAxisAlignedBoundingBox(scene.SelectedItem.Matrix); + var offset = new Vector3(); + switch (alignment) + { + case AxisAlignment.Min: + offset[axisIndex] = totalAABB.minXYZ[axisIndex] - childAABB.minXYZ[axisIndex]; + break; + + case AxisAlignment.Center: + offset[axisIndex] = totalAABB.Center[axisIndex] - childAABB.Center[axisIndex]; + break; + + case AxisAlignment.Max: + offset[axisIndex] = totalAABB.maxXYZ[axisIndex] - childAABB.maxXYZ[axisIndex]; + break; + + case AxisAlignment.SourceCoordinateSystem: + { + // move the object back to the origin + offset = -Vector3.Transform(Vector3.Zero, child.Matrix); + + // figure out how to move it back to the start center + if (firstSourceOrigin.X == double.MaxValue) + { + firstSourceOrigin = -offset; + } + + offset += firstSourceOrigin; + } + break; + } + transformDatas.Add(new TransformData() + { + TransformedObject = child, + RedoTransform = child.Matrix * Matrix4X4.CreateTranslation(offset), + UndoTransform = child.Matrix, + }); + } + + return transformDatas; + } + + private void AddAlignDelegates(int axisIndex, AxisAlignment alignment, Button alignButton) + { + alignButton.Click += (sender, e) => + { + if (scene.HasSelection) + { + var transformDatas = GetTransforms(axisIndex, alignment); + scene.UndoBuffer.AddAndDo(new TransformCommand(transformDatas)); + + //scene.SelectedItem.MaterialIndex = extruderIndexCanPassToClick; + scene.Invalidate(); + } + }; + + alignButton.MouseEnter += (s2, e2) => + { + if (scene.HasSelection) + { + // make a preview of the new positions + var transformDatas = GetTransforms(axisIndex, alignment); + scene.Children.Modify((list) => + { + foreach (var transform in transformDatas) + { + var copy = transform.TransformedObject.Clone(); + copy.Matrix = transform.RedoTransform; + copy.Color = new Color(Color.Gray, 126); + list.Add(copy); + } + }); + } + }; + + alignButton.MouseLeave += (s3, e3) => + { + if (scene.HasSelection) + { + // clear the preview of the new positions + scene.Children.Modify((list) => + { + for (int i = list.Count - 1; i >= 0; i--) + { + if (list[i].Color.Alpha0To255 == 126) + { + list.RemoveAt(i); + } + } + }); + } + }; + } + + internal enum AxisAlignment { Min, Center, Max, SourceCoordinateSystem }; + + private GuiWidget CreateAlignButton(int axisIndex, AxisAlignment alignment, string lable) + { + var smallMarginButtonFactory = theme.MenuButtonFactory; + var alignButton = smallMarginButtonFactory.Generate(lable); + alignButton.Margin = new BorderDouble(3, 0); + + AddAlignDelegates(axisIndex, alignment, alignButton); + + return alignButton; + } + + private GuiWidget AddAlignControls() + { + var widget = new IgnoredPopupWidget() + { + HAnchor = HAnchor.Fit, + VAnchor = VAnchor.Fit, + BackgroundColor = Color.White, + Padding = new BorderDouble(5, 5, 5, 0) + }; + + FlowLayoutWidget buttonPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + VAnchor = VAnchor.Fit, + HAnchor = HAnchor.Fit, + }; + widget.AddChild(buttonPanel); + + string[] axisNames = new string[] { "X", "Y", "Z" }; + for (int axisIndex = 0; axisIndex < 3; axisIndex++) + { + FlowLayoutWidget alignButtons = new FlowLayoutWidget(FlowDirection.LeftToRight) + { + HAnchor = HAnchor.Fit, + Padding = new BorderDouble(5) + }; + buttonPanel.AddChild(alignButtons); + + alignButtons.AddChild(new TextWidget(axisNames[axisIndex]) + { + VAnchor = VAnchor.Center, + Margin = new BorderDouble(0, 0, 3, 0) + }); + + alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Min, "Min")); + alignButtons.AddChild(new HorizontalSpacer()); + alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Center, "Center")); + alignButtons.AddChild(new HorizontalSpacer()); + alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Max, "Max")); + alignButtons.AddChild(new HorizontalSpacer()); + } + + var dualExtrusionAlignButton = theme.MenuButtonFactory.Generate("Align for Dual Extrusion".Localize()); + dualExtrusionAlignButton.Margin = new BorderDouble(21, 0); + dualExtrusionAlignButton.HAnchor = HAnchor.Left; + buttonPanel.AddChild(dualExtrusionAlignButton); + + AddAlignDelegates(0, AxisAlignment.SourceCoordinateSystem, dualExtrusionAlignButton); + + return widget; + } + public class InMemoryItem : ILibraryContentItem { private IObject3D existingItem; diff --git a/PartPreviewWindow/View3D/MeshViewerWidget.cs b/PartPreviewWindow/View3D/MeshViewerWidget.cs index ebf77e1ef..456697a6e 100644 --- a/PartPreviewWindow/View3D/MeshViewerWidget.cs +++ b/PartPreviewWindow/View3D/MeshViewerWidget.cs @@ -59,7 +59,7 @@ namespace MatterHackers.MeshVisualizer } } - public static class MatterialRendering + public static class MaterialRendering { public static Color Color(int materialIndex) { @@ -340,7 +340,7 @@ namespace MatterHackers.MeshVisualizer public static Color GetExtruderColor(int extruderIndex) { - return MatterialRendering.Color(extruderIndex); + return MaterialRendering.Color(extruderIndex); } public void CreateGlDataObject(IObject3D item) @@ -520,7 +520,7 @@ namespace MatterHackers.MeshVisualizer // check if we should be rendering materials (this overrides the other colors) if (this.RenderType == RenderTypes.Materials) { - drawColor = MatterialRendering.Color(item.WorldMaterialIndex(scene.RootItem)); + drawColor = MaterialRendering.Color(item.WorldMaterialIndex(scene.RootItem)); } if(drawColor.alpha != 255 diff --git a/PartPreviewWindow/View3D/SceneActions.cs b/PartPreviewWindow/View3D/SceneActions.cs index 09ed55eed..6a50c5571 100644 --- a/PartPreviewWindow/View3D/SceneActions.cs +++ b/PartPreviewWindow/View3D/SceneActions.cs @@ -28,23 +28,19 @@ either expressed or implied, of the FreeBSD Project. */ using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; -using MatterHackers.Localizations; -using MatterHackers.MeshVisualizer; -using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow { public static class SceneActions { - public static async void UngroupSelection(this InteractiveScene Scene, View3DWidget view3DWidget) + public static async void UngroupSelection(this InteractiveScene Scene) { if (Scene.HasSelection) { @@ -95,17 +91,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } }); - if (view3DWidget.HasBeenClosed) - { - return; - } - // leave no selection Scene.SelectedItem = null; } } - public static async void GroupSelection(this InteractiveScene Scene, View3DWidget view3DWidget) + public static async void GroupSelection(this InteractiveScene Scene) { if (Scene.HasChildren()) { @@ -119,11 +110,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Store the operation for undo/redo Scene.UndoBuffer.AddAndDo(operation); }); - - if (view3DWidget.HasBeenClosed) - { - return; - } } } @@ -135,7 +121,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); } - public static async void DuplicateSelection(this InteractiveScene Scene, View3DWidget view3DWidget) + public static async void DuplicateSelection(this InteractiveScene Scene) { if (Scene.HasSelection) { @@ -147,7 +133,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (originalItem is SelectionGroup) { - // the selection is a group of obects that need to be copied + // the selection is a group of objects that need to be copied var copyList = originalItem.Children.ToList(); Scene.SelectedItem = null; foreach(var item in copyList) @@ -170,8 +156,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var newName = agg_basics.GetNonCollidingName(originalItem.Name, Scene.Descendants().Select((d) => d.Name)); clonedItem.Name = newName; - // More usefull if it creates the part in the exact positon and then the user can move it. - // Consistent with othre software as well. LBB 2017-12-02 + // More useful if it creates the part in the exact position and then the user can move it. + // Consistent with other software as well. LBB 2017-12-02 //PlatingHelper.MoveToOpenPositionRelativeGroup(clonedItem, Scene.Children); return clonedItem; @@ -181,20 +167,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return null; }); - if (view3DWidget.HasBeenClosed) - { - return; - } - // it might come back null due to threading if (newItem != null) { - Scene.InsertNewItem(view3DWidget, newItem); + Scene.InsertNewItem(newItem); } } } - public static void InsertNewItem(this InteractiveScene Scene, View3DWidget view3DWidget, IObject3D newItem) + public static void InsertNewItem(this InteractiveScene Scene, IObject3D newItem) { // Reposition first item to bed center if (Scene.Children.Count == 0) @@ -217,7 +198,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Scene.UndoBuffer.Add(insertOperation); } - public static void DeleteSelection(this InteractiveScene Scene, View3DWidget view3DWidget) + public static void DeleteSelection(this InteractiveScene Scene) { if (Scene.HasSelection) { diff --git a/PartPreviewWindow/View3D/SideBar/ScaleControl.cs b/PartPreviewWindow/View3D/SideBar/ScaleControl.cs index 7f51c293d..05bdef6be 100644 --- a/PartPreviewWindow/View3D/SideBar/ScaleControl.cs +++ b/PartPreviewWindow/View3D/SideBar/ScaleControl.cs @@ -24,14 +24,13 @@ either expressed or implied, of the FreeBSD Project. */ //#define DoBooleanTest +using System.Collections.Generic; using MatterHackers.Agg; using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.VectorMath; -using System; -using System.Collections.Generic; -using MatterHackers.DataConverters3D; namespace MatterHackers.MatterControl.PartPreviewWindow { diff --git a/PartPreviewWindow/View3D/View3DWidget.cs b/PartPreviewWindow/View3D/View3DWidget.cs index cb5d8915f..333bce157 100644 --- a/PartPreviewWindow/View3D/View3DWidget.cs +++ b/PartPreviewWindow/View3D/View3DWidget.cs @@ -28,27 +28,21 @@ either expressed or implied, of the FreeBSD Project. */ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; -using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MatterHackers.Agg; -using MatterHackers.Agg.Image; using MatterHackers.Agg.OpenGlGui; using MatterHackers.Agg.Platform; -using MatterHackers.Agg.Transform; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; -using MatterHackers.MatterControl.DataStorage; using MatterHackers.MatterControl.Library; -using MatterHackers.MatterControl.PrinterCommunication; using MatterHackers.MatterControl.PrintLibrary; using MatterHackers.MeshVisualizer; using MatterHackers.PolygonMesh; @@ -63,11 +57,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private bool DoBooleanTest = false; private bool deferEditorTillMouseUp = false; - public GuiWidget bottomActionPanel; - public readonly int EditButtonHeight = 44; - private ObservableCollection materialButtons = new ObservableCollection(); private bool hasDrawn = false; private Color[] SelectionColors = new Color[] { new Color(131, 4, 66), new Color(227, 31, 61), new Color(255, 148, 1), new Color(247, 224, 23), new Color(143, 212, 1) }; @@ -153,205 +144,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // if the scene is invalidated invalidate the widget Scene.Invalidated += (s, e) => Invalidate(); - // add in the plater tools - { - bool isPrinterMode = meshViewerWidget.EditorMode == MeshViewerWidget.EditorType.Printer; - var buttonSpacing = theme.ButtonSpacing; - - var buttonView = new FlowLayoutWidget(); - buttonView.AddChild(new ImageWidget(AggContext.StaticData.LoadIcon((isPrinterMode) ? "bed.png" : "cube.png", IconColor.Theme)) - { - Margin = new BorderDouble(left: 10), - VAnchor = VAnchor.Center - }); - - var buttonText = (isPrinterMode) ? "Bed".Localize() : "Part".Localize(); - buttonView.AddChild(new TextButton(buttonText, theme) - { - Padding = new BorderDouble(8, 4, 0, 4) - }); - - var selectionActionBar = new OverflowBar(theme, buttonView) - { - VAnchor = VAnchor.Center | VAnchor.Fit, - HAnchor = HAnchor.Stretch, - }; - selectionActionBar.OverflowMenu.Name = "Bed Options Menu"; - selectionActionBar.OverflowMenu.PopDirection = Direction.Up; - selectionActionBar.OverflowMenu.DynamicPopupContent = () => theme.CreatePopupMenu(this.BedMenuActions(sceneContext)); - selectionActionBar.OverflowMenu.AlignToRightEdge = true; - selectionActionBar.OverflowMenu.DrawArrow = true; - - bottomActionPanel = new DisableablePanel(selectionActionBar, enabled: true) - { - BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor, - }; - - Button addButton = smallMarginButtonFactory.Generate("Insert".Localize(), AggContext.StaticData.LoadIcon("cube.png", 14, 14, IconColor.Theme)); - addButton.Margin = 0; - addButton.Click += (sender, e) => - { - UiThread.RunOnIdle(() => - { - AggContext.FileDialogs.OpenFileDialog( - new OpenFileDialogParams(ApplicationSettings.OpenDesignFileParams, multiSelect: true), - (openParams) => - { - this.LoadAndAddPartsToPlate(openParams.FileNames); - }); - }); - }; - selectionActionBar.AddChild(addButton); - - selectionActionBar.AddChild(this.CreateActionSeparator()); - - Button ungroupButton = smallMarginButtonFactory.Generate("Ungroup".Localize()); - ungroupButton.Name = "3D View Ungroup"; - ungroupButton.Margin = buttonSpacing; - ungroupButton.Click += (sender, e) => - { - this.Scene.UngroupSelection(this); - }; - this.Scene.SelectionChanged += (s, e) => - { - ungroupButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(ungroupButton); - - Button groupButton = smallMarginButtonFactory.Generate("Group".Localize()); - groupButton.Name = "3D View Group"; - groupButton.Margin = buttonSpacing; - groupButton.Click += (sender, e) => - { - this.Scene.GroupSelection(this); - }; - this.Scene.SelectionChanged += (s, e) => - { - groupButton.Enabled = this.Scene.HasSelection - && this.Scene.SelectedItem is SelectionGroup - && this.Scene.SelectedItem.Children.Count > 1; - }; - selectionActionBar.AddChild(groupButton); - - // this is closer to the old align button - if (false) - { - var absoluteButton = smallMarginButtonFactory.Generate("Absolute".Localize()); - absoluteButton.Margin = buttonSpacing; - absoluteButton.Click += (sender, e) => - { - if (this.Scene.HasSelection) - { - this.Scene.SelectedItem.Matrix = Matrix4X4.Identity; - } - }; - selectionActionBar.AddChild(absoluteButton); - } - - // put in the material options - var alignButton = new PopupMenuButton("Align".Localize(), theme) - { - PopDirection = Direction.Up, - PopupContent = this.AddAlignControls(), - AlignToRightEdge = true, - Margin = buttonSpacing - }; - this.Scene.SelectionChanged += (s, e) => - { - alignButton.Enabled = this.Scene.HasSelection - && this.Scene.SelectedItem is SelectionGroup - && this.Scene.SelectedItem.Children.Count > 1; - }; - selectionActionBar.AddChild(alignButton); - - var layFlatButton = smallMarginButtonFactory.Generate("Lay Flat".Localize()); - layFlatButton.Margin = buttonSpacing; - layFlatButton.Click += (sender, e) => - { - if (this.Scene.HasSelection) - { - MakeLowestFaceFlat(this.Scene.SelectedItem, Scene.RootItem); - } - }; - this.Scene.SelectionChanged += (s, e) => - { - layFlatButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(layFlatButton); - - selectionActionBar.AddChild(this.CreateActionSeparator()); - - var duplicateButton = smallMarginButtonFactory.Generate("Duplicate".Localize()); - duplicateButton.Name = "3D View Copy"; - duplicateButton.Margin = buttonSpacing; - duplicateButton.Click += (sender, e) => - { - this.Scene.DuplicateSelection(this); - }; - this.Scene.SelectionChanged += (s, e) => - { - duplicateButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(duplicateButton); - - var deleteButton = smallMarginButtonFactory.Generate("Remove".Localize()); - deleteButton.Name = "3D View Remove"; - deleteButton.Margin = buttonSpacing; - deleteButton.Click += (sender, e) => - { - this.Scene.DeleteSelection(this); - }; - this.Scene.SelectionChanged += (s, e) => - { - deleteButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(deleteButton); - - var mirrorButton = new PopupMenuButton("Mirror".Localize(), theme) - { - Name = "Mirror Button", - PopDirection = Direction.Up, - PopupContent = new MirrorControls(Scene), - AlignToRightEdge = true, - Margin = buttonSpacing, - }; - this.Scene.SelectionChanged += (s, e) => - { - mirrorButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(mirrorButton); - - var scaleButton = new PopupMenuButton("Scale".Localize(), theme) - { - Name = "Scale Button", - PopDirection = Direction.Up, - DynamicPopupContent = () => new ScaleControls(Scene, Color.Black), - AlignToRightEdge = true, - Margin = buttonSpacing, - }; - this.Scene.SelectionChanged += (s, e) => - { - scaleButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(scaleButton); - - // put in the material options - var materialsButton = new PopupMenuButton("Materials".Localize(), theme) - { - PopDirection = Direction.Up, - PopupContent = this.AddMaterialControls(), - AlignToRightEdge = true, - Margin = buttonSpacing - }; - this.Scene.SelectionChanged += (s, e) => - { - materialsButton.Enabled = this.Scene.HasSelection; - }; - selectionActionBar.AddChild(materialsButton); - } - - mainContainerTopToBottom.AddChild(bottomActionPanel); - this.AddChild(mainContainerTopToBottom); this.AnchorAll(); @@ -416,75 +208,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.viewControls3D.modelViewButton.Enabled = sceneContext.EditableScene; } - private NamedAction[] BedMenuActions(BedConfig sceneContext) - { - // Bed menu - return new[] - { - new NamedAction() - { - Title = "Save".Localize(), - Action = async () => - { - await ApplicationController.Instance.Tasks.Execute(this.SaveChanges); - }, - IsEnabled = () => sceneContext.EditableScene - }, - new NamedAction() - { - Title = "Save As".Localize(), - Action = () => UiThread.RunOnIdle(OpenSaveAsWindow), - IsEnabled = () => sceneContext.EditableScene - }, - new NamedAction() - { - Title = "Export".Localize(), - Action = () => - { - UiThread.RunOnIdle(() => - { - DialogWindow.Show( - new ExportPrintItemPage(new[] - { - new FileSystemFileItem(sceneContext.EditContext.PartFilePath) - })); - }); - }, - IsEnabled = () => sceneContext.EditableScene - }, - new NamedAction() - { - Title = "Publish".Localize(), - Action = () => - { - UiThread.RunOnIdle(() => DialogWindow.Show()); - }, - IsEnabled = () => sceneContext.EditableScene - }, - new NamedAction() - { - Title = "Arrange All Parts".Localize(), - Action = () => - { - this.Scene.AutoArrangeChildren(this); - }, - IsEnabled = () => sceneContext.EditableScene - }, - new NamedAction() { Title = "----" }, - new NamedAction() - { - Title = "Clear Bed".Localize(), - Action = () => - { - UiThread.RunOnIdle(() => - { - sceneContext.ClearPlate().ConfigureAwait(false); - }); - } - } - }; - } - private void SceneContext_SceneLoaded(object sender, EventArgs e) { if (this.printerTabPage?.printerActionsBar?.sliceButton is GuiWidget sliceButton) @@ -626,7 +349,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow case Keys.Delete: case Keys.Back: - this.Scene.DeleteSelection(this); + this.Scene.DeleteSelection(); break; case Keys.Escape: @@ -1419,232 +1142,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - internal GuiWidget AddAlignControls() - { - var widget = new IgnoredPopupWidget() - { - HAnchor = HAnchor.Fit, - VAnchor = VAnchor.Fit, - BackgroundColor = Color.White, - Padding = new BorderDouble(5, 5, 5, 0) - }; - - FlowLayoutWidget buttonPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) - { - VAnchor = VAnchor.Fit, - HAnchor = HAnchor.Fit, - }; - widget.AddChild(buttonPanel); - - string[] axisNames = new string[] { "X", "Y", "Z" }; - for (int axisIndex = 0; axisIndex < 3; axisIndex++) - { - FlowLayoutWidget alignButtons = new FlowLayoutWidget(FlowDirection.LeftToRight) - { - HAnchor = HAnchor.Fit, - Padding = new BorderDouble(5) - }; - buttonPanel.AddChild(alignButtons); - - alignButtons.AddChild(new TextWidget(axisNames[axisIndex]) - { - VAnchor = VAnchor.Center, - Margin = new BorderDouble(0, 0, 3, 0) - }); - - alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Min, "Min")); - alignButtons.AddChild(new HorizontalSpacer()); - alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Center, "Center")); - alignButtons.AddChild(new HorizontalSpacer()); - alignButtons.AddChild(CreateAlignButton(axisIndex, AxisAlignment.Max, "Max")); - alignButtons.AddChild(new HorizontalSpacer()); - } - - var dualExtrusionAlignButton = theme.MenuButtonFactory.Generate("Align for Dual Extrusion".Localize()); - dualExtrusionAlignButton.Margin = new BorderDouble(21, 0); - dualExtrusionAlignButton.HAnchor = HAnchor.Left; - buttonPanel.AddChild(dualExtrusionAlignButton); - - AddAlignDelegates(0, AxisAlignment.SourceCoordinateSystem, dualExtrusionAlignButton); - - return widget; - } - - internal enum AxisAlignment { Min, Center, Max, SourceCoordinateSystem }; - private GuiWidget CreateAlignButton(int axisIndex, AxisAlignment alignment, string lable) - { - var smallMarginButtonFactory = theme.MenuButtonFactory; - var alignButton = smallMarginButtonFactory.Generate(lable); - alignButton.Margin = new BorderDouble(3, 0); - - AddAlignDelegates(axisIndex, alignment, alignButton); - - return alignButton; - } - - private void AddAlignDelegates(int axisIndex, AxisAlignment alignment, Button alignButton) - { - alignButton.Click += (sender, e) => - { - if (Scene.HasSelection) - { - var transformDatas = GetTransforms(axisIndex, alignment); - this.Scene.UndoBuffer.AddAndDo(new TransformCommand(transformDatas)); - - //Scene.SelectedItem.MaterialIndex = extruderIndexCanPassToClick; - Scene.Invalidate(); - } - }; - - alignButton.MouseEnter += (s2, e2) => - { - if (Scene.HasSelection) - { - // make a preview of the new positions - var transformDatas = GetTransforms(axisIndex, alignment); - Scene.Children.Modify((list) => - { - foreach (var transform in transformDatas) - { - var copy = transform.TransformedObject.Clone(); - copy.Matrix = transform.RedoTransform; - copy.Color = new Color(Color.Gray, 126); - list.Add(copy); - } - }); - } - }; - - alignButton.MouseLeave += (s3, e3) => - { - if (Scene.HasSelection) - { - // clear the preview of the new positions - Scene.Children.Modify((list) => - { - for(int i=list.Count-1; i>=0; i--) - { - if (list[i].Color.Alpha0To255 == 126) - { - list.RemoveAt(i); - } - } - }); - } - }; - } - - private List GetTransforms(int axisIndex, AxisAlignment alignment) - { - var transformDatas = new List(); - var totalAABB = Scene.SelectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity); - - Vector3 firstSourceOrigin = new Vector3(double.MaxValue, double.MaxValue, double.MaxValue); - - // move the objects to the right place - foreach (var child in Scene.SelectedItem.Children) - { - var childAABB = child.GetAxisAlignedBoundingBox(Scene.SelectedItem.Matrix); - var offset = new Vector3(); - switch (alignment) - { - case AxisAlignment.Min: - offset[axisIndex] = totalAABB.minXYZ[axisIndex] - childAABB.minXYZ[axisIndex]; - break; - - case AxisAlignment.Center: - offset[axisIndex] = totalAABB.Center[axisIndex] - childAABB.Center[axisIndex]; - break; - - case AxisAlignment.Max: - offset[axisIndex] = totalAABB.maxXYZ[axisIndex] - childAABB.maxXYZ[axisIndex]; - break; - - case AxisAlignment.SourceCoordinateSystem: - { - // move the object back to the origin - offset = -Vector3.Transform(Vector3.Zero, child.Matrix); - - // figure out how to move it back to the start center - if(firstSourceOrigin.X == double.MaxValue) - { - firstSourceOrigin = -offset; - } - - offset += firstSourceOrigin; - } - break; - } - transformDatas.Add(new TransformData() - { - TransformedObject = child, - RedoTransform = child.Matrix * Matrix4X4.CreateTranslation(offset), - UndoTransform = child.Matrix, - }); - } - - return transformDatas; - } - - internal GuiWidget AddMaterialControls() - { - var widget = new IgnoredPopupWidget() - { - HAnchor = HAnchor.Fit, - VAnchor = VAnchor.Fit, - BackgroundColor = Color.White, - Padding = new BorderDouble(0, 5, 5, 0) - }; - - FlowLayoutWidget buttonPanel = new FlowLayoutWidget(FlowDirection.TopToBottom) - { - VAnchor = VAnchor.Fit, - HAnchor = HAnchor.Fit, - }; - widget.AddChild(buttonPanel); - - materialButtons.Clear(); - int extruderCount = 4; - for (int extruderIndex = 0; extruderIndex < extruderCount; extruderIndex++) - { - FlowLayoutWidget colorSelectionContainer = new FlowLayoutWidget(FlowDirection.LeftToRight) - { - HAnchor = HAnchor.Fit, - Padding = new BorderDouble(5) - }; - buttonPanel.AddChild(colorSelectionContainer); - - string materialLabelText = string.Format("{0} {1}", "Material".Localize(), extruderIndex + 1); - - RadioButton materialSelection = new RadioButton(materialLabelText, textColor: Color.Black); - materialButtons.Add(materialSelection); - materialSelection.SiblingRadioButtonList = materialButtons; - colorSelectionContainer.AddChild(materialSelection); - colorSelectionContainer.AddChild(new HorizontalSpacer()); - int extruderIndexCanPassToClick = extruderIndex; - materialSelection.Click += (sender, e) => - { - if (Scene.HasSelection) - { - Scene.SelectedItem.MaterialIndex = extruderIndexCanPassToClick; - Scene.Invalidate(); - - // "View 3D Overflow Menu" // the menu to click on - // "Materials Option" // the item to highlight - //HelpSystem. - } - }; - - colorSelectionContainer.AddChild(new GuiWidget(16, 16) - { - BackgroundColor = MatterialRendering.Color(extruderIndex), - Margin = new BorderDouble(5, 0, 0, 0) - }); - } - - return widget; - } - // TODO: Consider if we should always allow DragDrop or if we should prevent during printer or other scenarios private bool AllowDragDrop() => true; @@ -1688,25 +1185,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var selectedItem = Scene.SelectedItem; - if (materialButtons?.Count > 0) - { - bool setSelection = false; - // Set the material selector to have the correct material button selected - for (int i = 0; i < materialButtons.Count; i++) - { - if (selectedItem.MaterialIndex == i) - { - ((RadioButton)materialButtons[i]).Checked = true; - setSelection = true; - } - } - - if(!setSelection) - { - ((RadioButton)materialButtons[0]).Checked = true; - } - } - selectedObjectPanel.SetActiveItem(selectedItem); } @@ -1742,195 +1220,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - private async void LoadAndAddPartsToPlate(string[] filesToLoad) - { - if (filesToLoad != null && filesToLoad.Length > 0) - { - await Task.Run(() => loadAndAddPartsToPlate(filesToLoad)); - - if (HasBeenClosed) - { - return; - } - - bool addingOnlyOneItem = Scene.Children.Count == Scene.Children.Count + 1; - - if (Scene.HasChildren()) - { - if (addingOnlyOneItem) - { - // if we are only adding one part to the plate set the selection to it - Scene.SelectLastChild(); - } - } - - Scene.Invalidate(); - this.Invalidate(); - } - } - - private async Task loadAndAddPartsToPlate(string[] filesToLoadIncludingZips) - { - Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; - - if (filesToLoadIncludingZips?.Any() == true) - { - List filesToLoad = new List(); - foreach (string loadedFileName in filesToLoadIncludingZips) - { - string extension = Path.GetExtension(loadedFileName).ToUpper(); - if ((extension != "" && MeshFileIo.ValidFileExtensions().Contains(extension))) - { - filesToLoad.Add(loadedFileName); - } - else if (extension == ".ZIP") - { - List partFiles = ProjectFileHandler.ImportFromProjectArchive(loadedFileName); - if (partFiles != null) - { - foreach (PrintItem part in partFiles) - { - filesToLoad.Add(part.FileLocation); - } - } - } - } - - string progressMessage = "Loading Parts...".Localize(); - - var itemCache = new Dictionary(); - - foreach (string filePath in filesToLoad) - { - var libraryItem = new FileSystemFileItem(filePath); - - IObject3D object3D = null; - - await ApplicationController.Instance.Tasks.Execute(async (progressReporter, cancelationToken) => - { - var progressStatus = new ProgressStatus() - { - Status = "Loading ".Localize() + Path.GetFileName(filePath), - }; - - progressReporter.Report(progressStatus); - - object3D = await libraryItem.CreateContent((double progress0To1, string processingState) => - { - progressStatus.Progress0To1 = progress0To1; - progressStatus.Status = processingState; - progressReporter.Report(progressStatus); - }); - }); - - if (object3D != null) - { - Scene.Children.Modify(list => list.Add(object3D)); - - PlatingHelper.MoveToOpenPositionRelativeGroup(object3D, this.Scene.Children); - } - } - } - } - - internal 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); - } - public static Regex fileNameNumberMatch = new Regex("\\(\\d+\\)", RegexOptions.Compiled); private FlowLayoutWidget editorPanel; @@ -1958,7 +1247,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return Task.CompletedTask; } - private void OpenSaveAsWindow() + internal void OpenSaveAsWindow() { DialogWindow.Show( new SaveAsPage( diff --git a/PartPreviewWindow/ViewControls3D.cs b/PartPreviewWindow/ViewControls3D.cs index 273a4f359..9581d1ff3 100644 --- a/PartPreviewWindow/ViewControls3D.cs +++ b/PartPreviewWindow/ViewControls3D.cs @@ -28,15 +28,21 @@ either expressed or implied, of the FreeBSD Project. */ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.ImageProcessing; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; +using MatterHackers.MatterControl.DataStorage; +using MatterHackers.MatterControl.Library; +using MatterHackers.MeshVisualizer; using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow @@ -86,8 +92,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private EventHandler unregisterEvents; private PrinterConfig printer; + private View3DWidget view3DWidget; private ViewControls3DButtons activeTransformState = ViewControls3DButtons.Rotate; + public bool IsPrinterMode { get; set; } public ViewControls3DButtons ActiveButton { @@ -139,6 +147,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } + internal void SetView3DWidget(View3DWidget view3DWidget) + { + this.view3DWidget = view3DWidget; + } + public ViewControls3D(BedConfig sceneContext, ThemeConfig theme, UndoBuffer undoBuffer) : base (theme) { @@ -304,28 +317,50 @@ namespace MatterHackers.MatterControl.PartPreviewWindow buttonGroupB.Add(layers2DButton); this.AddChild(layers2DButton); - this.AddChild(new VerticalLine(50) + + Button addButton = theme.SmallMarginButtonFactory.Generate("Insert".Localize(), AggContext.StaticData.LoadIcon("cube.png", 14, 14, IconColor.Theme)); + addButton.Margin = 0; + addButton.Click += (sender, e) => { - Margin = 3, - Border = new BorderDouble(right: 5), - BorderColor = new Color(theme.ButtonFactory.Options.NormalTextColor, 100) + UiThread.RunOnIdle(() => + { + AggContext.FileDialogs.OpenFileDialog( + new OpenFileDialogParams(ApplicationSettings.OpenDesignFileParams, multiSelect: true), + (openParams) => + { + this.LoadAndAddPartsToPlate(openParams.FileNames, sceneContext.Scene); + }); + }); + }; + this.AddChild(addButton); + + var buttonSpacing = theme.ButtonSpacing; + + var buttonView = new FlowLayoutWidget(); + buttonView.AddChild(new ImageWidget(AggContext.StaticData.LoadIcon((IsPrinterMode) ? "bed.png" : "cube.png", IconColor.Theme)) + { + Margin = new BorderDouble(left: 10), + VAnchor = VAnchor.Center }); - foreach (var namedAction in ApplicationController.Instance.RegisteredSceneOperations()) + var buttonText = (IsPrinterMode) ? "Bed".Localize() : "Part".Localize(); + buttonView.AddChild(new TextButton(buttonText, theme) { - var button = new TextButton(namedAction.Title, theme) - { - Name = namedAction.Title + " Button", - VAnchor = VAnchor.Center, - Margin = theme.ButtonSpacing, - BackgroundColor = theme.MinimalShade - }; - button.Click += (s, e) => - { - namedAction.Action.Invoke(sceneContext.Scene); - }; - this.AddChild(button); - } + Padding = new BorderDouble(8, 4, 0, 4) + }); + + var overflowMenu = new OverflowMenu(buttonView) + { + Name = "Bed Options Menu", + DynamicPopupContent = () => theme.CreatePopupMenu(this.BedMenuActions(sceneContext)), + DrawArrow = true + }; + overflowMenu.Load += (s, e) => + { + var firstBackgroundColor = this.Parents().Where(p => p.BackgroundColor.Alpha0To1 == 1).FirstOrDefault()?.BackgroundColor; + overflowMenu.BackgroundColor = firstBackgroundColor ?? Color.Transparent; + }; + this.AddChild(overflowMenu); if (printer != null) { @@ -368,6 +403,164 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } + private async void LoadAndAddPartsToPlate(string[] filesToLoad, InteractiveScene scene) + { + if (filesToLoad != null && filesToLoad.Length > 0) + { + await Task.Run(() => loadAndAddPartsToPlate(filesToLoad, scene)); + + if (HasBeenClosed) + { + return; + } + + bool addingOnlyOneItem = scene.Children.Count == scene.Children.Count + 1; + + if (scene.HasChildren()) + { + if (addingOnlyOneItem) + { + // if we are only adding one part to the plate set the selection to it + scene.SelectLastChild(); + } + } + + scene.Invalidate(); + this.Invalidate(); + } + } + + private async Task loadAndAddPartsToPlate(string[] filesToLoadIncludingZips, InteractiveScene scene) + { + if (filesToLoadIncludingZips?.Any() == true) + { + List filesToLoad = new List(); + foreach (string loadedFileName in filesToLoadIncludingZips) + { + string extension = Path.GetExtension(loadedFileName).ToUpper(); + if ((extension != "" && MeshFileIo.ValidFileExtensions().Contains(extension))) + { + filesToLoad.Add(loadedFileName); + } + else if (extension == ".ZIP") + { + List partFiles = ProjectFileHandler.ImportFromProjectArchive(loadedFileName); + if (partFiles != null) + { + foreach (PrintItem part in partFiles) + { + filesToLoad.Add(part.FileLocation); + } + } + } + } + + string progressMessage = "Loading Parts...".Localize(); + + var itemCache = new Dictionary(); + + foreach (string filePath in filesToLoad) + { + var libraryItem = new FileSystemFileItem(filePath); + + IObject3D object3D = null; + + await ApplicationController.Instance.Tasks.Execute(async (progressReporter, cancelationToken) => + { + var progressStatus = new ProgressStatus() + { + Status = "Loading ".Localize() + Path.GetFileName(filePath), + }; + + progressReporter.Report(progressStatus); + + object3D = await libraryItem.CreateContent((double progress0To1, string processingState) => + { + progressStatus.Progress0To1 = progress0To1; + progressStatus.Status = processingState; + progressReporter.Report(progressStatus); + }); + }); + + if (object3D != null) + { + scene.Children.Modify(list => list.Add(object3D)); + + PlatingHelper.MoveToOpenPositionRelativeGroup(object3D, scene.Children); + } + } + } + } + + private NamedAction[] BedMenuActions(BedConfig sceneContext) + { + // Bed menu + return new[] + { + new NamedAction() + { + Title = "Save".Localize(), + Action = async () => + { + await ApplicationController.Instance.Tasks.Execute(view3DWidget.SaveChanges); + }, + IsEnabled = () => sceneContext.EditableScene + }, + new NamedAction() + { + Title = "Save As".Localize(), + Action = () => UiThread.RunOnIdle(view3DWidget.OpenSaveAsWindow), + IsEnabled = () => sceneContext.EditableScene + }, + new NamedAction() + { + Title = "Export".Localize(), + Action = () => + { + UiThread.RunOnIdle(() => + { + DialogWindow.Show( + new ExportPrintItemPage(new[] + { + new FileSystemFileItem(sceneContext.EditContext.PartFilePath) + })); + }); + }, + IsEnabled = () => sceneContext.EditableScene + }, + new NamedAction() + { + Title = "Publish".Localize(), + Action = () => + { + UiThread.RunOnIdle(() => DialogWindow.Show()); + }, + IsEnabled = () => sceneContext.EditableScene + }, + new NamedAction() + { + Title = "Arrange All Parts".Localize(), + Action = () => + { + sceneContext.Scene.AutoArrangeChildren(view3DWidget); + }, + IsEnabled = () => sceneContext.EditableScene + }, + new NamedAction() { Title = "----" }, + new NamedAction() + { + Title = "Clear Bed".Localize(), + Action = () => + { + UiThread.RunOnIdle(() => + { + sceneContext.ClearPlate().ConfigureAwait(false); + }); + } + } + }; + } + public override void OnClosed(ClosedEventArgs e) { unregisterEvents?.Invoke(this, null); diff --git a/Tests/MatterControl.AutomationTests/PartPreviewTests.cs b/Tests/MatterControl.AutomationTests/PartPreviewTests.cs index 08e1cb8cb..bf79f227d 100644 --- a/Tests/MatterControl.AutomationTests/PartPreviewTests.cs +++ b/Tests/MatterControl.AutomationTests/PartPreviewTests.cs @@ -39,12 +39,12 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.Select3DPart("Calibration - Box.stl"); // Click Copy button and count Scene.Children - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); testRunner.WaitFor(() => scene.Children.Count == 2); Assert.AreEqual(2, scene.Children.Count, "Should have 2 parts after copy"); // Click Copy button a second time and count Scene.Children - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); testRunner.WaitFor(() => scene.Children.Count > 2); Assert.AreEqual(3, scene.Children.Count, "Should have 3 parts after 2nd copy"); @@ -73,7 +73,7 @@ namespace MatterHackers.MatterControl.Tests.Automation for (int i = 2; i <= 6; i++) { - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); testRunner.WaitFor(() => scene.Children.Count == i); Assert.AreEqual(i, scene.Children.Count, $"Should have {i} parts after copy"); } @@ -84,11 +84,11 @@ namespace MatterHackers.MatterControl.Tests.Automation // select all testRunner.Type("^a"); - testRunner.ClickByName("3D View Group"); + testRunner.ClickByName("Group Button"); testRunner.WaitFor(() => scene.Children.Count == 1); Assert.AreEqual(1, scene.Children.Count, $"Should have 1 parts after group"); - testRunner.ClickByName("3D View Ungroup"); + testRunner.ClickByName("Ungroup Button"); testRunner.WaitFor(() => scene.Children.Count == 6); Assert.AreEqual(6, scene.Children.Count, $"Should have 6 parts after ungroup"); @@ -115,14 +115,14 @@ namespace MatterHackers.MatterControl.Tests.Automation // Add 5 items for (int i = 0; i <= 4; i++) { - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); testRunner.Delay(.5); } Assert.AreEqual(6, scene.Children.Count, "There should be 6 parts on the bed after the copy loop"); // Remove an item - testRunner.ClickByName("3D View Remove"); + testRunner.ClickByName("Remove Button"); // Confirm Assert.AreEqual(5, scene.Children.Count, "There should be 5 parts on the bed after remove"); @@ -146,7 +146,7 @@ namespace MatterHackers.MatterControl.Tests.Automation for (int i = 0; i <= 2; i++) { - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); testRunner.Delay(.5); } diff --git a/Tests/MatterControl.AutomationTests/SqLiteLibraryProvider.cs b/Tests/MatterControl.AutomationTests/SqLiteLibraryProvider.cs index 85d36b746..1b1167bc0 100644 --- a/Tests/MatterControl.AutomationTests/SqLiteLibraryProvider.cs +++ b/Tests/MatterControl.AutomationTests/SqLiteLibraryProvider.cs @@ -25,11 +25,11 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.Select3DPart("Calibration - Box.stl"); Assert.IsTrue(scene.HasSelection); - testRunner.ClickByName("3D View Copy"); + testRunner.ClickByName("Duplicate Button"); // wait for the copy to finish testRunner.Delay(.1); - testRunner.ClickByName("3D View Remove"); + testRunner.ClickByName("Remove Button"); testRunner.SaveBedplateToFolder("0Test Part", "Local Library Row Item Collection");