diff --git a/MatterControlLib/ApplicationView/SceneOperations.cs b/MatterControlLib/ApplicationView/SceneOperations.cs index 4e246b491..ca1451b0c 100644 --- a/MatterControlLib/ApplicationView/SceneOperations.cs +++ b/MatterControlLib/ApplicationView/SceneOperations.cs @@ -711,7 +711,7 @@ namespace MatterHackers.MatterControl }; } - private static bool BooleanCandidate(IObject3D selectedItem, bool includePaths = true) + private static bool BooleanCandidate(IObject3D selectedItem, bool includePaths) { if (selectedItem != null) { @@ -722,7 +722,6 @@ namespace MatterHackers.MatterControl return true; } - includePaths = false; // path items if (includePaths && selectedItem.VisiblePaths().Count() > 1 @@ -790,6 +789,7 @@ namespace MatterHackers.MatterControl PinchOperation(), TwistOperation(), PlaneCutOperation(), + FindSliceOperation(), HollowOutOperation(), } }, @@ -931,7 +931,7 @@ namespace MatterHackers.MatterControl { if (sceneContext.Scene.SelectedItem.VisiblePaths().Count() > 1) { - new MergePathObject3D("Combine".Localize(), true).WrapSelectedItemAndSelect(sceneContext.Scene); + new MergePathObject3D("Combine".Localize(), ClipperLib.ClipType.ctUnion).WrapSelectedItemAndSelect(sceneContext.Scene); } else { @@ -940,7 +940,7 @@ namespace MatterHackers.MatterControl }, Icon = (theme) => StaticData.Instance.LoadIcon("combine.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(), HelpTextResolver = () => "*At least 2 parts must be selected*".Localize(), - IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem), + IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem, true), }; } @@ -1133,7 +1133,7 @@ namespace MatterHackers.MatterControl { if (sceneContext.Scene.SelectedItem.VisiblePaths().Count() > 1) { - new MergePathObject3D("Intersect".Localize(), false).WrapSelectedItemAndSelect(sceneContext.Scene); + new MergePathObject3D("Intersect".Localize(), ClipperLib.ClipType.ctIntersection).WrapSelectedItemAndSelect(sceneContext.Scene); } else { @@ -1142,7 +1142,7 @@ namespace MatterHackers.MatterControl }, Icon = (theme) => StaticData.Instance.LoadIcon("intersect.png", 16, 16), HelpTextResolver = () => "*At least 2 parts must be selected*".Localize(), - IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem), + IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem, true), }; } @@ -1243,6 +1243,24 @@ namespace MatterHackers.MatterControl }; } + private static SceneOperation FindSliceOperation() + { + return new SceneOperation("Find Slice") + { + OperationType = typeof(IObject3D), + ResultType = typeof(PlaneCutObject3D), + TitleResolver = () => "Find Slice".Localize(), + Action = (sceneContext) => + { + var cut = new FindSliceObject3D(); + cut.WrapSelectedItemAndSelect(sceneContext.Scene); + }, + Icon = (theme) => StaticData.Instance.LoadIcon("plane_cut.png", 16, 16).SetToColor(theme.TextColor), + HelpTextResolver = () => "*At least 1 part must be selected*".Localize(), + IsEnabled = (sceneContext) => IsMeshObject(sceneContext.Scene.SelectedItem), + }; + } + private static SceneOperation RadialArrayOperation() { return new SceneOperation("Radial Array") @@ -1369,7 +1387,7 @@ namespace MatterHackers.MatterControl }, Icon = (theme) => StaticData.Instance.LoadIcon("subtract.png", 16, 16).SetPreMultiply(), HelpTextResolver = () => "*At least 2 parts must be selected*".Localize(), - IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem), + IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem, true), }; } diff --git a/MatterControlLib/DesignTools/Operations/ArrayLinearObject3D.cs b/MatterControlLib/DesignTools/Operations/ArrayLinearObject3D.cs index f158ab59f..0e96f37c7 100644 --- a/MatterControlLib/DesignTools/Operations/ArrayLinearObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/ArrayLinearObject3D.cs @@ -77,8 +77,9 @@ namespace MatterHackers.MatterControl.DesignTools.Operations var distance = Distance.Value(this); var count = Count.Value(this); - // add in all the array items - for (int i = 0; i < Math.Max(count, 1); i++) + + // add in all the array items + for (int i = 0; i < Math.Max(count, 1); i++) { var next = arrayItem.Clone(); next.Matrix = arrayItem.Matrix * Matrix4X4.CreateTranslation(Direction.Normal.GetNormal() * distance * i); diff --git a/MatterControlLib/DesignTools/Operations/FindSliceObject3D.cs b/MatterControlLib/DesignTools/Operations/FindSliceObject3D.cs new file mode 100644 index 000000000..eb70b6f51 --- /dev/null +++ b/MatterControlLib/DesignTools/Operations/FindSliceObject3D.cs @@ -0,0 +1,198 @@ +/* +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.Collections.Generic; +using System.Threading.Tasks; +using MatterHackers.Agg.UI; +using MatterHackers.Agg.VertexSource; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.PolygonMesh; +using MatterHackers.PolygonMesh.Csg; +using MatterHackers.VectorMath; +using Polygons = System.Collections.Generic.List>; + +namespace MatterHackers.MatterControl.DesignTools +{ + public class FindSliceObject3D : OperationSourceContainerObject3D, IPathObject, IPropertyGridModifier + { + public FindSliceObject3D() + { + Name = "Find Slice".Localize(); + } + + public double SliceHeight { get; set; } = 10; + + public IVertexSource VertexSource { get; set; } = new VertexStorage(); + + private double cutMargin = .01; + + public (Mesh mesh, Polygons polygons) Cut(IObject3D item) + { + var mesh = new Mesh(item.Mesh.Vertices, item.Mesh.Faces); + + var itemMatrix = item.WorldMatrix(this); + mesh.Transform(itemMatrix); + + // calculate and add the PWN face from the loops + var cutPlane = new Plane(Vector3.UnitZ, new Vector3(0, 0, SliceHeight)); + var slice = SliceLayer.CreateSlice(mesh, cutPlane); + + // copy every face that is on or below the cut plane + // cut the faces at the cut plane + mesh.Split(new Plane(Vector3.UnitZ, SliceHeight), cutMargin, cleanAndMerge: false); + + // remove every face above the cut plane + RemoveFacesAboveCut(mesh); + + slice.Vertices().TriangulateFaces(null, mesh, SliceHeight); + + mesh.Transform(itemMatrix.Inverted); + + return (mesh, slice); + } + + public override void Flatten(UndoBuffer undoBuffer) + { + var newPathObject = new PathObject3D() + { + VertexSource = new VertexStorage(this.VertexSource) + }; + + base.Flatten(undoBuffer, new IObject3D[] { newPathObject }); + } + + private void RemoveFacesAboveCut(Mesh mesh) + { + var newVertices = new List(); + var newFaces = new List(); + var facesToRemove = new HashSet(); + + var cutRemove = SliceHeight - cutMargin; + for (int i = 0; i < mesh.Faces.Count; i++) + { + var face = mesh.Faces[i]; + + if (mesh.Vertices[face.v0].Z >= cutRemove + && mesh.Vertices[face.v1].Z >= cutRemove + && mesh.Vertices[face.v2].Z >= cutRemove) + { + // record the face for removal + facesToRemove.Add(i); + } + } + + // make a new list of all the faces we are keeping + var keptFaces = new List(); + for (int i = 0; i < mesh.Faces.Count; i++) + { + if (!facesToRemove.Contains(i)) + { + keptFaces.Add(mesh.Faces[i]); + } + } + + var vertexCount = mesh.Vertices.Count; + + // add the new vertices + mesh.Vertices.AddRange(newVertices); + + // add the new faces (have to make the vertex indices to the new vertices + foreach (var newFace in newFaces) + { + Face faceNewIndices = newFace; + faceNewIndices.v0 += vertexCount; + faceNewIndices.v1 += vertexCount; + faceNewIndices.v2 += vertexCount; + keptFaces.Add(faceNewIndices); + } + + mesh.Faces = new FaceList(keptFaces); + } + + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + + var rebuildLocks = this.RebuilLockAll(); + + var valuesChanged = false; + + return TaskBuilder( + "Find Slice".Localize(), + (reporter, cancellationToken) => + { + var polygons = new Polygons(); + VertexSource = polygons.PolygonToPathStorage(); + + var newChildren = new List(); + foreach (var sourceItem in SourceContainer.VisibleMeshes()) + { + var meshPolygons = Cut(sourceItem); + + polygons = polygons.CreateUnion(meshPolygons.polygons); + + var newMesh = new Object3D() + { + Mesh = meshPolygons.mesh, + OwnerID = sourceItem.ID + }; + newMesh.CopyProperties(sourceItem, Object3DPropertyFlags.All); + newChildren.Add(newMesh); + } + + VertexSource = polygons.PolygonToPathStorage(); + + RemoveAllButSource(); + SourceContainer.Visible = false; + foreach (var child in newChildren) + { + this.Children.Add(child); + } + + UiThread.RunOnIdle(() => + { + rebuildLocks.Dispose(); + if (valuesChanged) + { + Invalidate(InvalidateType.DisplayValues); + } + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); + }); + + return Task.CompletedTask; + }); + } + + public void UpdateControls(PublicPropertyChange change) + { + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs index 9d52a55a7..7fa6ce390 100644 --- a/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Image/ImageToPathObject3D.cs @@ -40,6 +40,7 @@ using MatterHackers.Agg.Transform; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; +using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.Localizations; using MatterHackers.MarchingSquares; using MatterHackers.MatterControl.DesignTools.Operations; @@ -148,7 +149,8 @@ namespace MatterHackers.MatterControl.DesignTools private void UpdateHistogramDisplay() { - if (_histogramRawCache != null) + if (_histogramRawCache != null + && _histogramDisplayCache != null) { var graphics2D = _histogramDisplayCache.NewGraphics2D(); graphics2D.Clear(Color.Transparent); @@ -206,6 +208,13 @@ namespace MatterHackers.MatterControl.DesignTools this.DrawPath(); } + public override bool CanFlatten => true; + + public override void Flatten(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } + public void GenerateMarchingSquaresAndLines(Action progressReporter, ImageBuffer image, IThresholdFunction thresholdFunction) { if (image != null) diff --git a/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs b/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs index 2a55a6b51..4ad4fe97d 100644 --- a/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Image/PathObject3D.cs @@ -27,15 +27,36 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ -using ClipperLib; +using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; +using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.MatterControl.PartPreviewWindow; +using Newtonsoft.Json; using System.Collections.Generic; namespace MatterHackers.MatterControl.DesignTools { - public class PathObject3D : Object3D, IPathObject + public class PathObject3D : Object3D, IPathObject, ISelectedEditorDraw { - public IVertexSource VertexSource { get; set; } = new VertexStorage(); + [JsonIgnore] + private IVertexSource _vertexSource = new VertexStorage(); + + public IVertexSource VertexSource + { + get => _vertexSource; + + set + { + _vertexSource = value; + // set the mesh to show the path + this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight); + } + } + + public void DrawEditor(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) + { + this.DrawPath(); + } } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Operations/Object3DExtensions.cs b/MatterControlLib/DesignTools/Operations/Object3DExtensions.cs index 528a7a282..c938083da 100644 --- a/MatterControlLib/DesignTools/Operations/Object3DExtensions.cs +++ b/MatterControlLib/DesignTools/Operations/Object3DExtensions.cs @@ -35,6 +35,7 @@ using System.Text; using ClipperLib; using MatterHackers.Agg; using MatterHackers.Agg.Transform; +using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters2D; using MatterHackers.DataConverters3D; @@ -103,6 +104,32 @@ namespace MatterHackers.MatterControl.DesignTools.Operations return 0; } + public static void FlattenToPathObject(this IObject3D item, UndoBuffer undoBuffer) + { + if (item is IPathObject pathObject) + { + using (item.RebuildLock()) + { + var newPathObject = new PathObject3D(); + newPathObject.VertexSource = new VertexStorage(pathObject.VertexSource); + + // and replace us with the children + var replaceCommand = new ReplaceCommand(new[] { item }, new[] { newPathObject }); + + if (undoBuffer != null) + { + undoBuffer.AddAndDo(replaceCommand); + } + else + { + replaceCommand.Do(); + } + + newPathObject.MakeNameNonColliding(); + } + } + } + public static void DrawPath(this IObject3D item) { if (item is IPathObject pathObject) diff --git a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs index 6f03ad7e1..7bf9a8279 100644 --- a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs @@ -39,13 +39,15 @@ using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.Localizations; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.Plugins.EditorTools; using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; using Newtonsoft.Json; namespace MatterHackers.MatterControl.DesignTools.Operations { - public class LinearExtrudeObject3D : Object3D + public class LinearExtrudeObject3D : Object3D, IObject3DControlsProvider #if DEBUG , IPropertyGridModifier #endif @@ -84,6 +86,24 @@ namespace MatterHackers.MatterControl.DesignTools.Operations } } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + double getHeight() => Height.Value(this); + void setHeight(double height) => Height = height; + object3DControlsLayer.Object3DControls.Add(new ScaleHeightControl(object3DControlsLayer, + null, + null, + null, + null, + getHeight, + setHeight, + null, + null)); + object3DControlsLayer.AddControls(ControlTypes.ScaleMatrixXY); + object3DControlsLayer.AddControls(ControlTypes.MoveInZ); + object3DControlsLayer.AddControls(ControlTypes.RotateXYZ); + } + public override void Flatten(UndoBuffer undoBuffer) { if (Mesh == null) @@ -138,6 +158,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations public override Task Rebuild() { this.DebugDepth("Rebuild"); + var rebuildLock = RebuildLock(); bool valuesChanged = false; @@ -149,8 +170,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations var bevelInset = BevelInset.ClampIfNotCalculated(this, 0, Math.Min(aabb.XSize /2, aabb.YSize / 2), ref valuesChanged); #endif - var rebuildLock = RebuildLock(); - // now create a long running task to process the image + // now create a long running task to do the extrusion return ApplicationController.Instance.Tasks.Execute( "Linear Extrude".Localize(), null, @@ -188,6 +208,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations } Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); }); + return Task.CompletedTask; }); } diff --git a/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs index 0ef807882..50d556667 100644 --- a/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/MergePathObject3D.cs @@ -44,13 +44,13 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { public class MergePathObject3D : OperationSourceContainerObject3D, IPathObject, ISelectedEditorDraw, IObject3DControlsProvider { - private bool union; + private ClipperLib.ClipType clipType; private string operationName; - public MergePathObject3D(string name, bool union) + public MergePathObject3D(string name, ClipperLib.ClipType clipType) { this.operationName = name; - this.union = union; + this.clipType = clipType; Name = name; } @@ -61,6 +61,13 @@ namespace MatterHackers.MatterControl.DesignTools.Operations this.DrawPath(); } + public override bool CanFlatten => true; + + public override void Flatten(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) { object3DControlsLayer.AddControls(ControlTypes.Standard2D); @@ -135,14 +142,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { var itemVertexSource = pathItem.VertexSource.Transform(item.Matrix); - if (union) - { - resultsVertexSource = resultsVertexSource.Union(itemVertexSource); - } - else - { - resultsVertexSource = resultsVertexSource.MergePaths(itemVertexSource, ClipperLib.ClipType.ctIntersection); - } + resultsVertexSource = resultsVertexSource.MergePaths(itemVertexSource, clipType); percentCompleted += amountPerOperation; progressStatus.Progress0To1 = percentCompleted; diff --git a/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs b/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs new file mode 100644 index 000000000..996d995f8 --- /dev/null +++ b/MatterControlLib/DesignTools/Primitives/BoxPathObject3D.cs @@ -0,0 +1,132 @@ +/* +Copyright (c) 2019, 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.Collections.Generic; +using System.Threading.Tasks; +using MatterHackers.Agg.UI; +using MatterHackers.Agg.VertexSource; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools.Operations; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.PolygonMesh; +using Newtonsoft.Json; + +namespace MatterHackers.MatterControl.DesignTools +{ + public class BoxPathObject3D : PrimitiveObject3D, IPathObject, IObject3DControlsProvider, ISelectedEditorDraw + { + public BoxPathObject3D() + { + Name = "Box".Localize(); + Color = Operations.Object3DExtensions.PrimitiveColors["Cube"]; + } + + public override string ThumbnailName => "Box"; + + [JsonIgnore] + private IVertexSource _vertexSource = new VertexStorage(); + + public IVertexSource VertexSource + { + get => _vertexSource; + + set + { + _vertexSource = value; + // set the mesh to show the path + this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight); + } + } + + public void DrawEditor(Object3DControlsLayer layer, List transparentMeshes, DrawEventArgs e) + { + this.DrawPath(); + } + + /// + /// This is the actual serialized with that can use expressions + /// + [MaxDecimalPlaces(2)] + public DoubleOrExpression Width { get; set; } = 20; + + [MaxDecimalPlaces(2)] + public DoubleOrExpression Depth { get; set; } = 20; + + public static async Task Create() + { + var item = new BoxPathObject3D(); + await item.Rebuild(); + return item; + } + + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.MoveInZ); + object3DControlsLayer.AddWidthDepthControls(this, Width, Depth, null); + + object3DControlsLayer.AddControls(ControlTypes.MoveInZ); + object3DControlsLayer.AddControls(ControlTypes.RotateXYZ); + } + + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) + { + await Rebuild(); + } + else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + { + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateArgs); + } + } + + public override Task Rebuild() + { + this.DebugDepth("Rebuild"); + + using (RebuildLock()) + { + using (new CenterAndHeightMaintainer(this)) + { + var width = Width.Value(this); + var depth = Depth.Value(this); + VertexSource = new RoundedRect(-width / 2, -depth / 2, width / 2, depth / 2, 0); + } + } + + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs index 28690b647..1992ff0f7 100644 --- a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs @@ -115,10 +115,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations } public override void Flatten(UndoBuffer undoBuffer) + { + Flatten(undoBuffer, null); + } + + protected void Flatten(UndoBuffer undoBuffer, IEnumerable extraItems) { using (RebuildLock()) { - List newChildren = new List(); + var newChildren = new List(); // push our matrix into a copy of our children foreach (var child in this.Children) { @@ -210,7 +215,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { using (RebuildLock()) { - List newChildren = new List(); + var newChildren = new List(); // push our matrix into a copy of our children foreach (var child in this.SourceContainer.Children) { diff --git a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs index 0a3adc0ba..434c51825 100644 --- a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs +++ b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs @@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -274,6 +275,10 @@ namespace MatterHackers.MatterControl.DesignTools // could not find a sheet, try to evaluate the expression directly var evaluator = new Expression(inputExpression.ToLower()); + if(evaluator.checkSyntax()) + { + Debug.WriteLine(evaluator.getErrorMessage()); + } return CastResult(evaluator.calculate().ToString(), inputExpression); } @@ -282,7 +287,7 @@ namespace MatterHackers.MatterControl.DesignTools /// /// The item to start the search from /// - public static SheetObject3D Find(IObject3D item) + private static SheetObject3D Find(IObject3D item) { // look through all the parents foreach (var parent in item.Parents()) diff --git a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs index 225d13f0b..9d8570c91 100644 --- a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs @@ -31,12 +31,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.DesignTools; -using MatterHackers.MatterControl.SlicerConfiguration; namespace MatterHackers.MatterControl.Library { @@ -62,60 +60,60 @@ namespace MatterHackers.MatterControl.Library new GeneratorItem( () => "Cube".Localize(), async () => await CubeObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Pyramid".Localize(), async () => await PyramidObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Wedge".Localize(), async () => await WedgeObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Half Wedge".Localize(), async () => await HalfWedgeObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Text".Localize(), async () => await TextObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Cylinder".Localize(), async () => await CylinderObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Cone".Localize(), async () => await ConeObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Half Cylinder".Localize(), async () => await HalfCylinderObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Torus".Localize(), async () => await TorusObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Ring".Localize(), async () => await RingObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Sphere".Localize(), async () => await SphereObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Half Sphere".Localize(), async () => await HalfSphereObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, #if DEBUG new GeneratorItem( () => "SCAD Script".Localize(), async () => await OpenScadScriptObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "MarchingSquares".Localize(), async () => await MarchingSquaresObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, #endif new GeneratorItem( () => "Image Converter".Localize(), @@ -140,21 +138,21 @@ namespace MatterHackers.MatterControl.Library var constructedComponent = tempScene.SelectedItem; tempScene.Children.Remove(constructedComponent); - return Task.FromResult(constructedComponent); + return Task.FromResult(constructedComponent); }) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Measure Tool".Localize(), async () => await MeasureToolObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Description".Localize(), async () => await DescriptionObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, new GeneratorItem( () => "Variable Sheet".Localize(), async () => await SheetObject3D.Create()) - { DateCreated = new System.DateTime(index++) }, + { DateCreated = new DateTime(index++) }, }; string title = "Primitive Shapes".Localize(); @@ -164,6 +162,80 @@ namespace MatterHackers.MatterControl.Library item.Category = title; Items.Add(item); } + + this.ChildContainers.Add( + new DynamicContainerLink( + () => "Primitives 2D".Localize(), + StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")), + StaticData.Instance.LoadIcon(Path.Combine("Library", "primitives_library_icon.png")), + () => new Primitives2DContainer()) + { + IsReadOnly = true + }); + + } + } + + public class Primitives2DContainer : LibraryContainer + { + public Primitives2DContainer() + { + Name = "Primitives 2D".Localize(); + DefaultSort = new LibrarySortBehavior() + { + SortKey = SortKey.ModifiedDate, + Ascending = true, + }; + } + + public override void Load() + { + var library = ApplicationController.Instance.Library; + + long index = DateTime.Now.Ticks; + var libraryItems = new List() + { + new GeneratorItem( + () => "Box".Localize(), + async () => await BoxPathObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Triangle".Localize(), + async () => await PyramidObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Trapezoid".Localize(), + async () => await WedgeObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Text".Localize(), + async () => await TextPathObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Oval".Localize(), + async () => await CylinderObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Star".Localize(), + async () => await ConeObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Ring".Localize(), + async () => await RingObject3D.Create()) + { DateCreated = new DateTime(index++) }, + new GeneratorItem( + () => "Circle".Localize(), + async () => await SphereObject3D.Create()) + { DateCreated = new DateTime(index++) }, + }; + + string title = "2D Shapes".Localize(); + + foreach (var item in libraryItems) + { + item.Category = title; + Items.Add(item); + } } } } diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs index e3115789d..6bb582438 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs @@ -89,6 +89,13 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D } } + public override bool CanFlatten => true; + + public override void Flatten(UndoBuffer undoBuffer) + { + this.FlattenToPathObject(undoBuffer); + } + public override Task Rebuild() { this.DebugDepth("Rebuild"); @@ -112,7 +119,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D } // set the mesh to show the path - this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight); + var extrudeMesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight); + if(extrudeMesh.Vertices.Count() > 5) + { + this.Mesh = extrudeMesh; + } + else + { + this.Mesh = null; + } UiThread.RunOnIdle(() => {