diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index 28397ec4f..ebc3b7cd1 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -728,12 +728,13 @@ namespace MatterHackers.MatterControl "Rotate".Localize(), (sceneItem, scene) => { - if (sceneItem is IObject3D imageObject) - { - var path = new RotateObject3D(); - sceneItem.WrapWith(path, scene); - path.Invalidate(new InvalidateArgs(path, InvalidateType.Properties, null)); - } + var selectedItem = scene.SelectedItem; + scene.SelectedItem = null; + var rotate = RotateObject3D_2.Create(selectedItem.Clone()); + rotate.MakeNameNonColliding(); + + scene.UndoBuffer.AddAndDo(new ReplaceCommand(new List { selectedItem }, new List { rotate })); + scene.SelectedItem = rotate; return Task.CompletedTask; }, diff --git a/DesignTools/Interfaces/IPropertyGridModifier.cs b/DesignTools/Interfaces/IPropertyGridModifier.cs index 1e5156d7d..7959e7caa 100644 --- a/DesignTools/Interfaces/IPropertyGridModifier.cs +++ b/DesignTools/Interfaces/IPropertyGridModifier.cs @@ -51,6 +51,11 @@ namespace MatterHackers.MatterControl.DesignTools } } + public interface ITransformWarpperObject3D + { + IObject3D TransformWarpper { get; } + } + public interface IPropertyGridModifier { void UpdateControls(PPEContext editor); diff --git a/DesignTools/Operations/ArrayRadialObject3D.cs b/DesignTools/Operations/ArrayRadialObject3D.cs index ab0639a04..f702d7239 100644 --- a/DesignTools/Operations/ArrayRadialObject3D.cs +++ b/DesignTools/Operations/ArrayRadialObject3D.cs @@ -31,6 +31,8 @@ using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.DesignTools.EditableTypes; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.MeshVisualizer; using MatterHackers.VectorMath; using System; using System.ComponentModel; @@ -38,7 +40,7 @@ using System.Linq; namespace MatterHackers.MatterControl.DesignTools.Operations { - public class ArrayRadialObject3D : Object3D + public class ArrayRadialObject3D : Object3D, IEditorDraw { public ArrayRadialObject3D() { @@ -123,10 +125,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations this.Children.Modify(list => { list.Clear(); - // add back in the sourceContainer - list.Add(sourceContainer); - // get the source item - var sourceItem = sourceContainer.Children.First(); + // add back in the sourceContainer + list.Add(sourceContainer); + // get the source item + var sourceItem = sourceContainer.Children.First(); var offset = Vector3.Zero; for (int i = 0; i < Math.Max(Count, 1); i++) @@ -157,5 +159,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations base.Remove(undoBuffer); } + + public void DrawEditor(object sender, DrawEventArgs e) + { + if (sender is InteractionLayer layer + && layer.Scene.SelectedItem != null + && layer.Scene.SelectedItem.DescendantsAndSelf().Where((i) => i == this).Any()) + { + layer.World.RenderDirectionAxis(Axis, this.WorldMatrix(), 30); + } + } } } \ No newline at end of file diff --git a/DesignTools/Operations/FitToBoundsObject3D.cs b/DesignTools/Operations/FitToBoundsObject3D.cs index b187eaf20..2979df1fd 100644 --- a/DesignTools/Operations/FitToBoundsObject3D.cs +++ b/DesignTools/Operations/FitToBoundsObject3D.cs @@ -47,7 +47,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations public enum MaintainRatio { None, X_Y, X_Y_Z } - public class FitToBoundsObject3D : Object3D, IEditorDraw, IPropertyGridModifier + public class FitToBoundsObject3D : TransformWrapperObject3D, IEditorDraw, IPropertyGridModifier { [Description("Set the shape the part will be fit into.")] public FitType FitType { get; set; } = FitType.Box; @@ -66,59 +66,11 @@ namespace MatterHackers.MatterControl.DesignTools.Operations [Description("Allows you turn turn on and off applying the fit to the z axis.")] public bool StretchZ { get; set; } = true; - IObject3D ScaleItem => Children.First(); - [JsonIgnore] - public IObject3D ItemToScale => Children.First().Children.First(); - public FitToBoundsObject3D() { Name = "Fit to Bounds".Localize(); } - public override void Apply(UndoBuffer undoBuffer) - { - using (RebuildLock()) - { - // push our matrix into our children - foreach (var child in this.Children) - { - child.Matrix *= this.Matrix; - } - - // push child into children - ItemToScale.Matrix *= ScaleItem.Matrix; - - // add our children to our parent and remove from parent - this.Parent.Children.Modify(list => - { - list.Remove(this); - list.AddRange(ScaleItem.Children); - }); - } - Invalidate(new InvalidateArgs(this, InvalidateType.Content)); - } - - public override void Remove(UndoBuffer undoBuffer) - { - using (RebuildLock()) - { - // push our matrix into inner children - foreach (var child in ScaleItem.Children) - { - child.Matrix *= this.Matrix; - } - - // add inner children to our parent and remove from parent - this.Parent.Children.Modify(list => - { - list.Remove(this); - list.AddRange(ScaleItem.Children); - }); - } - - Invalidate(new InvalidateArgs(this, InvalidateType.Content)); - } - public override void OnInvalidate(InvalidateArgs invalidateType) { if ((invalidateType.InvalidateType == InvalidateType.Content @@ -142,7 +94,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations public static FitToBoundsObject3D Create(IObject3D itemToFit) { - FitToBoundsObject3D fitToBounds = new FitToBoundsObject3D(); + var fitToBounds = new FitToBoundsObject3D(); var aabb = itemToFit.GetAxisAlignedBoundingBox(); fitToBounds.Width = aabb.XSize; @@ -201,8 +153,8 @@ namespace MatterHackers.MatterControl.DesignTools.Operations private void AdjustChildSize(object sender, EventArgs e) { - var aabb = ItemToScale.GetAxisAlignedBoundingBox(); - ScaleItem.Matrix = Matrix4X4.Identity; + var aabb = SourceItem.GetAxisAlignedBoundingBox(); + TransformItem.Matrix = Matrix4X4.Identity; var scale = Vector3.One; if (StretchX) { @@ -234,7 +186,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations break; } - ScaleItem.Matrix = Object3DExtensions.ApplyAtPosition(ScaleItem.Matrix, aabb.Center, Matrix4X4.CreateScale(scale)); + TransformItem.Matrix = Object3DExtensions.ApplyAtPosition(TransformItem.Matrix, aabb.Center, Matrix4X4.CreateScale(scale)); } public void DrawEditor(object sender, DrawEventArgs e) @@ -243,7 +195,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations && layer.Scene.SelectedItem != null && layer.Scene.SelectedItem.DescendantsAndSelf().Where((i) => i == this).Any()) { - var aabb = ItemToScale.GetAxisAlignedBoundingBox(); + var aabb = SourceItem.GetAxisAlignedBoundingBox(); if (FitType == FitType.Box) { @@ -262,8 +214,6 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { layer.World.RenderCylinderOutline(this.WorldMatrix(), aabb.Center, Diameter, Height, 30, Color.Red, 1, 1); } - // turn the lighting back on - GL.Enable(EnableCap.Lighting); } } diff --git a/DesignTools/Operations/RotateObject3D.cs b/DesignTools/Operations/RotateObject3D.cs index 7b0f1a273..5422f9c39 100644 --- a/DesignTools/Operations/RotateObject3D.cs +++ b/DesignTools/Operations/RotateObject3D.cs @@ -38,16 +38,15 @@ using MatterHackers.VectorMath; using Newtonsoft.Json; using System.ComponentModel; using System.Linq; +using System; namespace MatterHackers.MatterControl.DesignTools.Operations { public enum RotationCenter { ObjectCenter, ObjectOrigin }; + //[Obsolete("Not used anymore. Replaced with RotedObject3D_2", true)] public class RotateObject3D : Object3D, IEditorDraw { - //public RotationCenter RotationCenter { get; set; } = RotationCenter.ObjectCenter; - //public Vector3 OffsetFromOrigin { get; set; } = Vector3.Zero; - [DisplayName("X")] [Description("Rotate about the X axis")] public double RotationXDegrees { get; set; } diff --git a/DesignTools/Operations/RotateObject3D_2.cs b/DesignTools/Operations/RotateObject3D_2.cs new file mode 100644 index 000000000..65c55fbe5 --- /dev/null +++ b/DesignTools/Operations/RotateObject3D_2.cs @@ -0,0 +1,161 @@ +/* +Copyright (c) 2018, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.MeshVisualizer; +using MatterHackers.RenderOpenGl.OpenGl; +using MatterHackers.VectorMath; +using Newtonsoft.Json; +using System.ComponentModel; +using System.Linq; +using MatterHackers.MatterControl.DesignTools.EditableTypes; +using System; + +namespace MatterHackers.MatterControl.DesignTools.Operations +{ + public class RotateObject3D_2 : TransformWrapperObject3D, IEditorDraw + { + public DirectionAxis Axis { get; set; } = new DirectionAxis() { Origin = Vector3.NegativeInfinity, Normal = Vector3.UnitZ }; + [DisplayName("Angle")] + public double AngleDegrees { get; set; } = 0; + + public RotateObject3D_2() + { + Name = "Rotate".Localize(); + } + + public RotateObject3D_2(IObject3D item, double xRadians = 0, double yRadians = 0, double zRadians = 0, string name = "") + { + Children.Add(item.Clone()); + + Rebuild(null); + } + + public RotateObject3D_2(IObject3D item, Vector3 translation, string name = "") + : this(item, translation.X, translation.Y, translation.Z, name) + { + } + + [JsonIgnore] + public Matrix4X4 RotationMatrix + { + get + { + var angleRadians = MathHelper.DegreesToRadians(AngleDegrees); + var rotation = Matrix4X4.CreateTranslation(-Axis.Origin) + * Matrix4X4.CreateRotation(Axis.Normal, angleRadians) + * Matrix4X4.CreateTranslation(Axis.Origin); + + return rotation; + } + } + + private void Rebuild(UndoBuffer undoBuffer) + { + this.DebugDepth("Rebuild"); + + using (RebuildLock()) + { + var startingAabb = this.GetAxisAlignedBoundingBox(); + + // remove the current rotation + TransformItem.Matrix = RotationMatrix; + + if (startingAabb.ZSize > 0) + { + // If the part was already created and at a height, maintain the height. + PlatingHelper.PlaceMeshAtHeight(this, startingAabb.minXYZ.Z); + } + } + + Invalidate(new InvalidateArgs(this, InvalidateType.Matrix, null)); + } + + public override void OnInvalidate(InvalidateArgs invalidateType) + { + if ((invalidateType.InvalidateType == InvalidateType.Content + || invalidateType.InvalidateType == InvalidateType.Matrix + || invalidateType.InvalidateType == InvalidateType.Mesh) + && invalidateType.Source != this + && !RebuildLocked) + { + Rebuild(null); + } + else if (invalidateType.InvalidateType == InvalidateType.Color) + { + var sourceItem = OperationSourceObject3D.GetOrCreateSourceContainer(this).Children.FirstOrDefault(); + foreach (var item in Children) + { + if (item != sourceItem) + { + item.Color = sourceItem.Color; + } + } + + base.OnInvalidate(invalidateType); + } + else if (invalidateType.InvalidateType == InvalidateType.Properties + && invalidateType.Source == this) + { + Rebuild(null); + } + else + { + base.OnInvalidate(invalidateType); + } + } + + public void DrawEditor(object sender, DrawEventArgs e) + { + if (sender is InteractionLayer layer + && layer.Scene.SelectedItem != null + && layer.Scene.SelectedItem.DescendantsAndSelf().Where((i) => i == this).Any()) + { + layer.World.RenderDirectionAxis(Axis, this.WorldMatrix(), 30); + } + } + + public static RotateObject3D_2 Create(IObject3D itemToRotate) + { + var rotate = new RotateObject3D_2(); + var aabb = itemToRotate.GetAxisAlignedBoundingBox(); + + rotate.Axis.Origin = aabb.Center; + + var rotateItem = new Object3D(); + rotate.Children.Add(rotateItem); + rotateItem.Children.Add(itemToRotate); + + return rotate; + } + } +} \ No newline at end of file diff --git a/DesignTools/Operations/TransformWrapperObject3D.cs b/DesignTools/Operations/TransformWrapperObject3D.cs new file mode 100644 index 000000000..648097d45 --- /dev/null +++ b/DesignTools/Operations/TransformWrapperObject3D.cs @@ -0,0 +1,102 @@ +/* +Copyright (c) 2018, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using System; +using System.ComponentModel; +using System.Linq; +using MatterHackers.Agg; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.MeshVisualizer; +using MatterHackers.RenderOpenGl; +using MatterHackers.RenderOpenGl.OpenGl; +using MatterHackers.VectorMath; +using Newtonsoft.Json; + +namespace MatterHackers.MatterControl.DesignTools.Operations +{ + public abstract class TransformWrapperObject3D : Object3D + { + protected IObject3D TransformItem => Children.First(); + + [JsonIgnore] + public IObject3D SourceItem => Children.First().Children.First(); + + public TransformWrapperObject3D() + { + Name = "Transform Wrapper".Localize(); + } + + public override void Apply(UndoBuffer undoBuffer) + { + using (RebuildLock()) + { + // push our matrix into our children + foreach (var child in this.Children) + { + child.Matrix *= this.Matrix; + } + + // push child into children + SourceItem.Matrix *= TransformItem.Matrix; + + // add our children to our parent and remove from parent + this.Parent.Children.Modify(list => + { + list.Remove(this); + list.AddRange(TransformItem.Children); + }); + } + Invalidate(new InvalidateArgs(this, InvalidateType.Content)); + } + + public override void Remove(UndoBuffer undoBuffer) + { + using (RebuildLock()) + { + // push our matrix into inner children + foreach (var child in TransformItem.Children) + { + child.Matrix *= this.Matrix; + } + + // add inner children to our parent and remove from parent + this.Parent.Children.Modify(list => + { + list.Remove(this); + list.AddRange(TransformItem.Children); + }); + } + + Invalidate(new InvalidateArgs(this, InvalidateType.Content)); + } + } +} \ No newline at end of file diff --git a/MatterControl.csproj b/MatterControl.csproj index d844dfe2d..d8a79b495 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -97,6 +97,7 @@ + @@ -110,6 +111,7 @@ + diff --git a/PartPreviewWindow/Object3DTreeBuilder.cs b/PartPreviewWindow/Object3DTreeBuilder.cs index 011aa092f..fd5c7be05 100644 --- a/PartPreviewWindow/Object3DTreeBuilder.cs +++ b/PartPreviewWindow/Object3DTreeBuilder.cs @@ -124,10 +124,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { switch (item) { - case FitToBoundsObject3D fitToBounds3D: + case TransformWrapperObject3D fitToBounds3D: return new ObjectView() { - Children = new IObject3D[] { fitToBounds3D.ItemToScale }, + Children = new IObject3D[] { fitToBounds3D.SourceItem }, Name = item.Name, Source = item }; diff --git a/PartPreviewWindow/View3D/MeshViewerWidget.cs b/PartPreviewWindow/View3D/MeshViewerWidget.cs index 6bf788257..ef5d3af7b 100644 --- a/PartPreviewWindow/View3D/MeshViewerWidget.cs +++ b/PartPreviewWindow/View3D/MeshViewerWidget.cs @@ -39,6 +39,7 @@ using MatterHackers.Agg.OpenGlGui; using MatterHackers.Agg.UI; using MatterHackers.DataConverters3D; using MatterHackers.MatterControl; +using MatterHackers.MatterControl.DesignTools.EditableTypes; using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.PartPreviewWindow.View3D; @@ -89,6 +90,9 @@ namespace MatterHackers.MeshVisualizer world.Render3DLineNoPrep(frustum, topStart, topEnd, color, lineWidth); world.Render3DLineNoPrep(frustum, bottomStart, bottomEnd, color, lineWidth); } + + // turn the lighting back on + GL.Enable(EnableCap.Lighting); } public static void RenderAabb(this WorldView world, AxisAlignedBoundingBox bounds, Matrix4X4 matrix, Color color, double width, double extendLineLength = 0) @@ -158,6 +162,56 @@ namespace MatterHackers.MeshVisualizer GL.Enable(EnableCap.Lighting); } + public static void RenderDirectionAxis(this WorldView world, DirectionAxis axis, Matrix4X4 matrix, double size) + { + GLHelper.PrepareFor3DLineRender(true); + + Frustum frustum = world.GetClippingFrustum(); + Vector3 length = axis.Normal * size; + var color = Agg.Color.Red; + + // draw center line + { + var min = axis.Origin - length; + Vector3 start = Vector3.Transform(min, matrix); + + var max = axis.Origin + length; + Vector3 end = Vector3.Transform(max, matrix); + + world.Render3DLineNoPrep(frustum, start, end, color, 1); + } + + var perpendicular = Vector3.GetPerpendicular(axis.Normal, Vector3.Zero).GetNormal(); + // draw some lines to mark the rotation plane + int count = 20; + bool first = true; + var firstEnd = Vector3.Zero; + var lastEnd = Vector3.Zero; + var center = Vector3.Transform(axis.Origin, matrix); + for (int i = 0; i < count; i++) + { + var rotation = size/4 * Vector3.Transform(perpendicular, Matrix4X4.CreateRotation(axis.Normal, MathHelper.Tau * i / count)); + // draw center line + var max = axis.Origin + rotation; + Vector3 end = Vector3.Transform(max, matrix); + + world.Render3DLineNoPrep(frustum, center, end, color, 1); + if (!first) + { + world.Render3DLineNoPrep(frustum, end, lastEnd, color, 1); + } + else + { + firstEnd = end; + } + lastEnd = end; + first = false; + } + world.Render3DLineNoPrep(frustum, firstEnd, lastEnd, color, 1); + + GL.Enable(EnableCap.Lighting); + } + public static Color Color(int materialIndex) { return ColorF.FromHSL(Math.Max(materialIndex, 0) / 10.0, .99, .49).ToColor();