Making paths behave more consistently

This commit is contained in:
Lars Brubaker 2020-10-16 16:25:11 -07:00
parent 7c5a764162
commit 864266bd7a
18 changed files with 466 additions and 98 deletions

View file

@ -704,7 +704,7 @@ namespace MatterHackers.MatterControl
};
}
private static bool BooleanCandidate(IObject3D selectedItem)
private static bool BooleanCandidate(IObject3D selectedItem, bool includePaths = true)
{
if (selectedItem != null)
{
@ -715,8 +715,10 @@ namespace MatterHackers.MatterControl
return true;
}
includePaths = false;
// path items
if (selectedItem.VisiblePaths().Count() > 1
if (includePaths
&& selectedItem.VisiblePaths().Count() > 1
&& selectedItem.VisiblePaths().All(i => IsPathObject(i)))
{
return true;
@ -877,8 +879,6 @@ namespace MatterHackers.MatterControl
RegisterIconsAndIdsRecursive(operation);
}
// TODO: Use custom selection group icon if reusing group icon seems incorrect
//
// Explicitly register SelectionGroup icon
if (Icons.TryGetValue(typeof(GroupObject3D), out Func<bool, ImageBuffer> groupIconSource))
{
@ -889,11 +889,24 @@ namespace MatterHackers.MatterControl
PrimaryOperations.Add(typeof(ImageObject3D), new List<SceneOperation> { SceneOperations.ById("ImageConverter"), SceneOperations.ById("ImageToPath"), });
// path operations
PrimaryOperations.Add(typeof(ImageToPathObject3D), new List<SceneOperation> { SceneOperations.ById("LinearExtrude"), SceneOperations.ById("SmoothPath") });
PrimaryOperations.Add(typeof(SmoothPathObject3D), new List<SceneOperation> { SceneOperations.ById("LinearExtrude"), SceneOperations.ById("InflatePath"), SceneOperations.ById("OutlinePath") });
PrimaryOperations.Add(typeof(InflatePathObject3D), new List<SceneOperation> { SceneOperations.ById("LinearExtrude"), SceneOperations.ById("OutlinePath") });
PrimaryOperations.Add(typeof(OutlinePathObject3D), new List<SceneOperation> { SceneOperations.ById("LinearExtrude"), SceneOperations.ById("InflatePath") });
PrimaryOperations.Add(typeof(ImageToPathObject3D), new List<SceneOperation>
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("SmoothPath")
});
PrimaryOperations.Add(typeof(SmoothPathObject3D), new List<SceneOperation>
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("InflatePath"), SceneOperations.ById("OutlinePath")
});
PrimaryOperations.Add(typeof(InflatePathObject3D), new List<SceneOperation>
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("OutlinePath")
});
PrimaryOperations.Add(typeof(OutlinePathObject3D), new List<SceneOperation>
{
SceneOperations.ById("LinearExtrude"), SceneOperations.ById("Revolve"), SceneOperations.ById("InflatePath")
});
// default operations
PrimaryOperations.Add(typeof(Object3D), new List<SceneOperation> { SceneOperations.ById("Scale") });
Icons.Add(typeof(ImageObject3D), (invertIcon) => AggContext.StaticData.LoadIcon("image_converter.png", 16, 16, invertIcon).SetPreMultiply());
@ -911,7 +924,7 @@ namespace MatterHackers.MatterControl
{
if (sceneContext.Scene.SelectedItem.VisiblePaths().Count() > 1)
{
new CombinePathObject3D().WrapSelectedItemAndSelect(sceneContext.Scene);
new MergePathObject3D("Combine".Localize(), true).WrapSelectedItemAndSelect(sceneContext.Scene);
}
else
{
@ -1109,7 +1122,17 @@ namespace MatterHackers.MatterControl
OperationType = typeof(IObject3D),
ResultType = typeof(IntersectionObject3D_2),
TitleResolver = () => "Intersect".Localize(),
Action = (sceneContext) => new IntersectionObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
Action = (sceneContext) =>
{
if (sceneContext.Scene.SelectedItem.VisiblePaths().Count() > 1)
{
new MergePathObject3D("Intersect".Localize(), false).WrapSelectedItemAndSelect(sceneContext.Scene);
}
else
{
new IntersectionObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene);
}
},
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("intersect.png", 16, 16),
HelpTextResolver = () => "*At least 2 parts must be selected*".Localize(),
IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem),
@ -1127,8 +1150,7 @@ namespace MatterHackers.MatterControl
{
return item != null
&& !(item is ImageObject3D)
&& (item is IPathObject)
&& (item.Mesh == null);
&& (item is IPathObject);
}
private static SceneOperation LayFlatOperation()
@ -1316,7 +1338,7 @@ namespace MatterHackers.MatterControl
Action = (sceneContext) => new SubtractAndReplaceObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract_and_replace.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, false),
};
}
@ -1327,7 +1349,17 @@ namespace MatterHackers.MatterControl
OperationType = typeof(IObject3D),
ResultType = typeof(SubtractObject3D_2),
TitleResolver = () => "Subtract".Localize(),
Action = (sceneContext) => new SubtractObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
Action = (sceneContext) =>
{
if (sceneContext.Scene.SelectedItem.VisiblePaths().Count() > 1)
{
new SubtractPathObject3D().WrapSelectedItemAndSelect(sceneContext.Scene);
}
else
{
new SubtractObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene);
}
},
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract.png", 16, 16).SetPreMultiply(),
HelpTextResolver = () => "*At least 2 parts must be selected*".Localize(),
IsEnabled = (sceneContext) => BooleanCandidate(sceneContext.Scene.SelectedItem),

View file

@ -0,0 +1,44 @@
/*
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.
*/
namespace MatterHackers.MatterControl.DesignTools
{
public static class Constants
{
/// <summary>
/// Gets the height of the geometry that shows the shape of a path object (it cannot be used in mesh operations).
/// </summary>
public static double PathPolygonsHeight => .2;
/// <summary>
/// Gets the alpha of the non z-buffered lines in the editor
/// </summary>
public static int LineAlpha => 30;
}
}

View file

@ -34,6 +34,7 @@ using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MeshVisualizer;
@ -202,7 +203,7 @@ namespace MatterHackers.Plugins.EditorTools
else
{
// render on top of everything very lightly
Object3DControlContext.World.Render3DLine(clippingFrustum, startPosition, endPosition, new Color(theme.TextColor, 20), false, GuiWidget.DeviceScale);
Object3DControlContext.World.Render3DLine(clippingFrustum, startPosition, endPosition, new Color(theme.TextColor, Constants.LineAlpha), false, GuiWidget.DeviceScale);
}
}

View file

@ -184,7 +184,7 @@ namespace MatterHackers.Plugins.EditorTools
else
{
// render on top of everything very lightly
Object3DControlContext.World.Render3DLine(clippingFrustum, bottomPosition, topPosition, theme.TextColor.WithAlpha(20), false, GuiWidget.DeviceScale);
Object3DControlContext.World.Render3DLine(clippingFrustum, bottomPosition, topPosition, theme.TextColor.WithAlpha(Constants.LineAlpha), false, GuiWidget.DeviceScale);
}
}
}

View file

@ -33,6 +33,7 @@ using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MeshVisualizer;
@ -188,7 +189,7 @@ namespace MatterHackers.Plugins.EditorTools
else
{
// render on top of everything very lightly
Object3DControlContext.World.Render3DLine(clippingFrustum, bottomPosition, topPosition, theme.TextColor.WithAlpha(20), false, GuiWidget.DeviceScale);
Object3DControlContext.World.Render3DLine(clippingFrustum, bottomPosition, topPosition, theme.TextColor.WithAlpha(Constants.LineAlpha), false, GuiWidget.DeviceScale);
}
}
}

View file

@ -317,6 +317,7 @@ namespace MatterHackers.MatterControl.DesignTools
RangeStart = 1 - minSeparation;
}
}
propertyUpdated = true;
}

View file

@ -95,6 +95,8 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
using (RebuildLock())
{
InsetPath();
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
}
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path));

View file

@ -147,9 +147,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
}
Mesh = VertexSourceToMesh.Extrude(this.VertexSource,
Height,
bevel);
Mesh = VertexSourceToMesh.Extrude(this.VertexSource, Height, bevel);
if (Mesh.Vertices.Count == 0)
{
Mesh = null;

View file

@ -36,19 +36,22 @@ using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.PolygonMesh;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.DesignTools.Operations
{
public class CombinePathObject3D : OperationSourceContainerObject3D, IPathObject, IEditorDraw, IObject3DControlsProvider
public class MergePathObject3D : OperationSourceContainerObject3D, IPathObject, IEditorDraw, IObject3DControlsProvider
{
public CombinePathObject3D()
private bool union;
private string operationName;
public MergePathObject3D(string name, bool union)
{
Name = "Combine";
this.operationName = name;
this.union = union;
Name = name;
}
public IVertexSource VertexSource { get; set; } = new VertexStorage();
@ -70,7 +73,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Combine".Localize(),
operationName,
null,
(reporter, cancellationToken) =>
{
@ -79,24 +82,22 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
try
{
Combine(cancellationToken, reporter);
Merge(reporter, cancellationToken);
}
catch
{
}
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
rebuildLocks.Dispose();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
return Task.CompletedTask;
});
}
public void Combine()
{
Combine(CancellationToken.None, null);
}
private void Combine(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
private void Merge(IProgress<ProgressStatus> reporter, CancellationToken cancellationToken)
{
SourceContainer.Visible = true;
RemoveAllButSource();
@ -131,7 +132,14 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
var itemVertexSource = pathItem.VertexSource.Transform(item.Matrix);
resultsVertexSource = resultsVertexSource.Union(itemVertexSource);
if (union)
{
resultsVertexSource = resultsVertexSource.Union(itemVertexSource);
}
else
{
resultsVertexSource = resultsVertexSource.MergePaths(itemVertexSource, ClipperLib.ClipType.ctIntersection);
}
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
@ -140,8 +148,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
this.VertexSource = resultsVertexSource;
// set the mesh to show the path
this.Mesh = resultsVertexSource.Extrude(.1);
SourceContainer.Visible = false;
}
}

View file

@ -104,7 +104,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
InsetPath();
// set the mesh to show the path
this.Mesh = VertexSource.Extrude(.1);
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
}
if (valuesChanged)

View file

@ -94,6 +94,9 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
DoSmoothing((long)(SmoothDistance * 1000), Iterations);
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
rebuildLock.Dispose();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Path));
return Task.CompletedTask;

View file

@ -659,58 +659,3 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
}
public static class Extensions
{
public static IVertexSource Minus(this IVertexSource a, IVertexSource b)
{
return CombinePaths(a, b, ClipType.ctDifference);
}
public static IVertexSource Plus(this IVertexSource a, IVertexSource b)
{
return CombinePaths(a, b, ClipType.ctUnion);
}
public static IVertexSource RotateZDegrees(this IVertexSource a, double angle)
{
return new VertexSourceApplyTransform(a, Affine.NewRotation(MathHelper.DegreesToRadians(angle)));
}
public static IVertexSource Subtract(this IVertexSource a, IVertexSource b)
{
return a.Minus(b);
}
public static IVertexSource Translate(this IVertexSource a, Vector2 delta)
{
return new VertexSourceApplyTransform(a, Affine.NewTranslation(delta));
}
public static IVertexSource Union(this IVertexSource a, IVertexSource b)
{
return a.Plus(b);
}
private static VertexStorage CombinePaths(IVertexSource a, IVertexSource b, ClipType clipType)
{
List<List<IntPoint>> aPolys = a.CreatePolygons();
List<List<IntPoint>> bPolys = b.CreatePolygons();
var clipper = new Clipper();
clipper.AddPaths(aPolys, PolyType.ptSubject, true);
clipper.AddPaths(bPolys, PolyType.ptClip, true);
var outputPolys = new List<List<IntPoint>>();
clipper.Execute(clipType, outputPolys);
Clipper.CleanPolygons(outputPolys);
VertexStorage output = outputPolys.CreateVertexStorage();
output.Add(0, 0, ShapePath.FlagsAndCommand.Stop);
return output;
}
}

View file

@ -171,7 +171,7 @@ namespace MatterHackers.MatterControl.DesignTools
var end = PositionsHaveBeenSet ? EndPosition : EndPosition.Transform(Matrix);
// draw on top of anything that is already drawn
object3DControlLayer.World.Render3DLine(start, end, Color.Black.WithAlpha(20), false, width: GuiWidget.DeviceScale);
object3DControlLayer.World.Render3DLine(start, end, Color.Black.WithAlpha(Constants.LineAlpha), false, width: GuiWidget.DeviceScale);
// Restore DepthTest
object3DControlLayer.World.Render3DLine(start, end, Color.Black, true, width: GuiWidget.DeviceScale);

View file

@ -0,0 +1,91 @@
/*
Involute Spur Gear Builder (c) 2014 Dr. Rainer Hessmer
ported to C# 2019 by Lars Brubaker
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 ClipperLib;
using MatterHackers.Agg;
using MatterHackers.Agg.Transform;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters2D;
using MatterHackers.VectorMath;
public static class VertexSourceExtensions
{
public static IVertexSource Minus(this IVertexSource a, IVertexSource b)
{
return MergePaths(a, b, ClipType.ctDifference);
}
public static IVertexSource Plus(this IVertexSource a, IVertexSource b)
{
return MergePaths(a, b, ClipType.ctUnion);
}
public static IVertexSource RotateZDegrees(this IVertexSource a, double angle)
{
return new VertexSourceApplyTransform(a, Affine.NewRotation(MathHelper.DegreesToRadians(angle)));
}
public static IVertexSource Subtract(this IVertexSource a, IVertexSource b)
{
return a.Minus(b);
}
public static IVertexSource Translate(this IVertexSource a, Vector2 delta)
{
return new VertexSourceApplyTransform(a, Affine.NewTranslation(delta));
}
public static IVertexSource Union(this IVertexSource a, IVertexSource b)
{
return a.Plus(b);
}
public static VertexStorage MergePaths(this IVertexSource a, IVertexSource b, ClipType clipType)
{
List<List<IntPoint>> aPolys = a.CreatePolygons();
List<List<IntPoint>> bPolys = b.CreatePolygons();
var clipper = new Clipper();
clipper.AddPaths(aPolys, PolyType.ptSubject, true);
clipper.AddPaths(bPolys, PolyType.ptClip, true);
var outputPolys = new List<List<IntPoint>>();
clipper.Execute(clipType, outputPolys);
Clipper.CleanPolygons(outputPolys);
VertexStorage output = outputPolys.CreateVertexStorage();
output.Add(0, 0, ShapePath.FlagsAndCommand.Stop);
return output;
}
}

View file

@ -50,7 +50,6 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.DesignTools
{
public class PublicPropertyEditor : IObject3DEditor
{
public string Name => "Property Editor";
@ -484,7 +483,16 @@ namespace MatterHackers.MatterControl.DesignTools
rowContainer = CreateSettingsColumn(property);
if (property.Item is OperationSourceContainerObject3D sourceContainer)
{
rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme));
Action selected = null;
if (!(context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() is ShowUpdateButtonAttribute showUpdate))
{
selected = () =>
{
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
};
}
rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme, selected));
}
else
{
@ -715,7 +723,7 @@ namespace MatterHackers.MatterControl.DesignTools
return rowContainer;
}
private static GuiWidget CreateSourceChildSelector(SelectedChildren childSelector, OperationSourceContainerObject3D sourceContainer, ThemeConfig theme)
private static GuiWidget CreateSourceChildSelector(SelectedChildren childSelector, OperationSourceContainerObject3D sourceContainer, ThemeConfig theme, Action selectionChanged)
{
GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
@ -783,6 +791,8 @@ namespace MatterHackers.MatterControl.DesignTools
childSelector.Remove(objectChecks[checkbox].ID);
}
}
selectionChanged?.Invoke();
}
};

View file

@ -33,7 +33,6 @@ using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CsvHelper.Configuration.Attributes;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;

View file

@ -0,0 +1,234 @@
/*
Copyright (c) 2017, 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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.RenderOpenGl;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class SubtractPathObject3D : OperationSourceContainerObject3D, IPathObject, IEditorDraw, IObject3DControlsProvider
{
public SubtractPathObject3D()
{
Name = "Subtract";
}
[DisplayName("Part(s) to Subtract")]
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
public IVertexSource VertexSource { get; set; } = new VertexStorage();
public void DrawEditor(Object3DControlsLayer layer, List<Object3DView> transparentMeshes, DrawEventArgs e)
{
this.DrawPath();
}
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
object3DControlsLayer.AddControls(ControlTypes.Standard2D);
}
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties)
&& invalidateType.Source == this)
{
await Rebuild();
}
else
{
base.OnInvalidate(invalidateType);
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Subtract(cancellationToken, reporter);
}
catch
{
}
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);
rebuildLocks.Dispose();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
private void Subtract(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var parentOfSubtractTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
if (parentOfSubtractTargets.Children.Count() < 2)
{
if (parentOfSubtractTargets.Children.Count() == 1)
{
this.Children.Add(SourceContainer.Clone());
SourceContainer.Visible = false;
}
return;
}
CleanUpSelectedChildrenNames(this);
var removeVisibleItems = parentOfSubtractTargets.Children
.Where((i) => SelectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisiblePaths())
.ToList();
var keepItems = parentOfSubtractTargets.Children
.Where((i) => !SelectedChildren
.Contains(i.ID));
var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePaths()).ToList();
if (removeVisibleItems.Any()
&& keepVisibleItems.Any())
{
var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
var progressStatus = new ProgressStatus
{
Status = "Do Subtract"
};
bool first = true;
foreach (var keep in keepVisibleItems)
{
var resultsVertexSource = (keep as IPathObject).VertexSource.Transform(keep.Matrix);
foreach (var remove in removeVisibleItems)
{
resultsVertexSource = resultsVertexSource.MergePaths(((IPathObject)remove).VertexSource.Transform(remove.Matrix), ClipperLib.ClipType.ctDifference);
// report our progress
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
if (first)
{
this.VertexSource = resultsVertexSource;
first = false;
}
else
{
this.VertexSource.MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion);
}
}
// this.VertexSource = this.VertexSource.Transform(Matrix.Inverted);
first = true;
foreach (var child in Children)
{
if (first)
{
// hide the source item
child.Visible = false;
first = false;
}
else
{
child.Visible = true;
}
}
}
}
public static void CleanUpSelectedChildrenNames(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
var parentOfSubtractTargets = item.DescendantsAndSelfMultipleChildrenFirstOrSelf();
var allVisibleNames = parentOfSubtractTargets.Children.Select(i => i.ID);
// remove any names from SelectedChildren that are not a child we can select
foreach (var name in selectableChildContainer.SelectedChildren.ToArray())
{
if (!allVisibleNames.Contains(name))
{
selectableChildContainer.SelectedChildren.Remove(name);
}
}
}
}
}
}

@ -1 +1 @@
Subproject commit a531015747da18b0e1608821c9a03be09b78e9b3
Subproject commit be1102ff3ec0a5410399dc7da38c625583763970