Consolidate scene selection operations, revise styling

This commit is contained in:
John Lewin 2018-01-09 15:47:00 -08:00
parent 56f5b64026
commit c3c098d005
13 changed files with 206 additions and 200 deletions

View file

@ -300,9 +300,74 @@ namespace MatterHackers.MatterControl
private List<SceneSelectionOperation> registeredSceneOperations = new List<SceneSelectionOperation>()
{
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<PrintItemAction>();
}
public IEnumerable<SceneSelectionOperation> RegisteredSceneOperations()
{
return registeredSceneOperations;
}
public IEnumerable<SceneSelectionOperation> RegisteredSceneOperations => registeredSceneOperations;
public event EventHandler<WidgetSourceEventArgs> AddPrintersTabRightElement;

View file

@ -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<string> TitleResolver { get; set; }
public string Title => this.TitleResolver?.Invoke();
public ImageBuffer Icon { get; set; }
}
public class SceneSelectionOperation : LocalizedAction
{
public Action<InteractiveScene> Action { get; set; }
public Func<InteractiveScene, bool> IsEnabled { get; set; }
}
public static class NamedActionExtensions

View file

@ -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);

View file

@ -56,7 +56,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private View3DWidget view3DWidget;
private InteractiveScene scene;
private PrinterConfig printer;
private List<NamedAction> sceneActions;
private Dictionary<Type, HashSet<IObject3DEditor>> objectEditorsByType;
private ObservableCollection<GuiWidget> materialButtons = new ObservableCollection<GuiWidget>();
private SectionWidget editorSection;
@ -75,61 +74,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
this.scene = scene;
this.printer = printer;
sceneActions = new List<NamedAction>
{
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<Vector3> faceVertices = new List<Vector3>();
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<TransformData> GetTransforms(int axisIndex, AxisAlignment alignment)
{
var transformDatas = new List<TransformData>();

View file

@ -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<Vector3> faceVertices = new List<Vector3>();
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<TransformCommand> allUndoTransforms = new List<TransformCommand>();

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

BIN
StaticData/Icons/group.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

BIN
StaticData/Icons/paint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
StaticData/Icons/remove.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,023 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB