Making paths behave more consistently
This commit is contained in:
parent
7c5a764162
commit
864266bd7a
18 changed files with 466 additions and 98 deletions
|
|
@ -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),
|
||||
|
|
|
|||
44
MatterControlLib/DesignTools/Constants.cs
Normal file
44
MatterControlLib/DesignTools/Constants.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
RangeStart = 1 - minSeparation;
|
||||
}
|
||||
}
|
||||
|
||||
propertyUpdated = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue