integrating offline changes

This commit is contained in:
Lars Brubaker 2023-03-10 17:15:55 -08:00
parent 121623bad3
commit 3f8eeda65b
125 changed files with 5442 additions and 5434 deletions

View file

@ -44,141 +44,140 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[ShowUpdateButton]
public class CombineObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread
{
[ShowUpdateButton]
public class CombineObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread
{
private CancellationTokenSource cancellationToken;
public CombineObject3D_2()
{
Name = "Combine";
}
{
Name = "Combine";
}
#if DEBUG
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#else
private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
private IplicitSurfaceMethod MeshAnalysis { get; set; }
private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#endif
public bool IsBuilding => this.cancellationToken != null;
public bool IsBuilding => this.cancellationToken != null;
public static void CheckManifoldData(CombineObject3D_2 item, IObject3D result)
{
bool IsManifold(Mesh mesh)
{
var meshEdgeList = mesh.NewMeshEdges();
public static void CheckManifoldData(CombineObject3D_2 item, IObject3D result)
{
bool IsManifold(Mesh mesh)
{
var meshEdgeList = mesh.NewMeshEdges();
foreach (var meshEdge in meshEdgeList)
{
if (meshEdge.Faces.Count() != 2)
{
return false;
}
}
foreach (var meshEdge in meshEdgeList)
{
if (meshEdge.Faces.Count() != 2)
{
return false;
}
}
return true;
}
return true;
}
if (!IsManifold(result.Mesh))
{
// create a new combine of a and b and add it to the root
var combine = new CombineObject3D_2();
if (!IsManifold(result.Mesh))
{
// create a new combine of a and b and add it to the root
var combine = new CombineObject3D_2();
var participants = item.SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(item.SourceContainer) != PrintOutputTypes.Hole);
// all participants are manifold
foreach (var participant in participants)
{
combine.SourceContainer.Children.Add(new Object3D()
{
Mesh = participant.Mesh.Copy(new CancellationToken()),
Matrix = participant.Matrix
});
}
var participants = item.SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(item.SourceContainer) != PrintOutputTypes.Hole);
// all participants are manifold
foreach (var participant in participants)
{
combine.SourceContainer.Children.Add(new Object3D()
{
Mesh = participant.Mesh.Copy(new CancellationToken()),
Matrix = participant.Matrix
});
}
var scene = result.Parents().Last();
scene.Children.Add(combine);
}
}
var scene = result.Parents().Last();
scene.Children.Add(combine);
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Combine".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource;
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
"Combine".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource;
reporter?.Invoke(0, null);
try
{
Combine(cancellationTokenSource.Token, reporter);
try
{
Combine(cancellationTokenSource.Token, reporter);
if (cancellationToken.IsCancellationRequested)
if (cancellationToken.IsCancellationRequested)
{
// the combine was canceled set our children to the source object children
SourceContainer.Visible = true;
RemoveAllButSource();
Children.Modify((list) =>
{
foreach (var child in SourceContainer.Children)
{
list.Add(child);
}
});
// the combine was canceled set our children to the source object children
SourceContainer.Visible = true;
RemoveAllButSource();
Children.Modify((list) =>
{
foreach (var child in SourceContainer.Children)
{
list.Add(child);
}
});
SourceContainer.Visible = false;
}
}
catch
{
}
SourceContainer.Visible = false;
}
}
catch
{
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
});
return Task.CompletedTask;
});
}
return Task.CompletedTask;
});
}
public void Combine()
{
Combine(CancellationToken.None, null);
}
public void Combine()
{
Combine(CancellationToken.None, null);
}
public override string NameFromChildren()
{
return CalculateName(SourceContainer.Children, " + ");
}
return CalculateName(SourceContainer.Children, " + ");
}
private void Combine(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
private void Combine(CancellationToken cancellationToken, Action<double, string> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
@ -238,27 +237,27 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem);
#if DEBUG
//resultsItem.Mesh.MergeVertices(.01);
//resultsItem.Mesh.CleanAndMerge();
//CheckManifoldData(this, resultsItem);
//resultsItem.Mesh.MergeVertices(.01);
//resultsItem.Mesh.CleanAndMerge();
//CheckManifoldData(this, resultsItem);
#endif
SourceContainer.Visible = false;
SourceContainer.Visible = false;
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if(threadSafe != null)
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
threadSafe.Cancel();
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -42,25 +42,25 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[ShowUpdateButton]
public class IntersectionObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier
{
public IntersectionObject3D_2()
{
Name = "Intersection";
}
[ShowUpdateButton]
public class IntersectionObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier
{
public IntersectionObject3D_2()
{
Name = "Intersection";
}
#if DEBUG
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#else
private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
@ -68,90 +68,88 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#endif
private CancellationTokenSource cancellationToken;
private CancellationTokenSource cancellationToken;
public bool IsBuilding => this.cancellationToken != null;
public bool IsBuilding => this.cancellationToken != null;
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Intersection".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
return ApplicationController.Instance.Tasks.Execute(
"Intersection".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
try
{
Intersect(cancellationTokenSource.Token, reporter);
}
catch
{
}
try
{
Intersect(cancellationTokenSource.Token, reporter);
}
catch
{
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
return Task.CompletedTask;
});
}
public override string NameFromChildren()
{
return CalculateName(SourceContainer.Children, " & ");
}
public override string NameFromChildren()
{
return CalculateName(SourceContainer.Children, " & ");
}
public void Intersect()
{
Intersect(CancellationToken.None, null);
}
public void Intersect()
{
Intersect(CancellationToken.None, null);
}
private void Intersect(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
private void Intersect(CancellationToken cancellationToken, Action<double, string> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var participants = SourceContainer.VisibleMeshes();
if (participants.Count() < 2)
{
if (participants.Count() == 1)
{
var newMesh = new Object3D();
newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All);
newMesh.Mesh = participants.First().Mesh;
this.Children.Add(newMesh);
SourceContainer.Visible = false;
}
return;
}
var participants = SourceContainer.VisibleMeshes();
if (participants.Count() < 2)
{
if (participants.Count() == 1)
{
var newMesh = new Object3D();
newMesh.CopyProperties(participants.First(), Object3DPropertyFlags.All);
newMesh.Mesh = participants.First().Mesh;
this.Children.Add(newMesh);
SourceContainer.Visible = false;
}
return;
}
var items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer)));
var items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer)));
#if false
var resultsMesh = BooleanProcessing.DoArray(items,
CsgModes.Intersect,
@ -161,69 +159,65 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
reporter,
cancellationToken);
#else
var totalOperations = items.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
var totalOperations = items.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
var progressStatus = new ProgressStatus();
var resultsMesh = items.First().Item1;
var keepWorldMatrix = items.First().Item2;
var resultsMesh = items.First().Item1;
var keepWorldMatrix = items.First().Item2;
bool first = true;
foreach (var next in items)
{
if (first)
{
first = false;
continue;
}
bool first = true;
foreach (var next in items)
{
if (first)
{
first = false;
continue;
}
resultsMesh = BooleanProcessing.Do(resultsMesh,
keepWorldMatrix,
// other mesh
next.Item1,
next.Item2,
// operation type
CsgModes.Intersect,
Processing,
InputResolution,
OutputResolution,
// reporting
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
resultsMesh = BooleanProcessing.Do(resultsMesh,
keepWorldMatrix,
// other mesh
next.Item1,
next.Item2,
// operation type
CsgModes.Intersect,
Processing,
InputResolution,
OutputResolution,
// reporting
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
// after the first time we get a result the results mesh is in the right coordinate space
keepWorldMatrix = Matrix4X4.Identity;
// after the first time we get a result the results mesh is in the right coordinate space
keepWorldMatrix = Matrix4X4.Identity;
// report our progress
ratioCompleted += amountPerOperation;
progressStatus.Progress0To1 = ratioCompleted;
reporter?.Report(progressStatus);
}
// report our progress
ratioCompleted += amountPerOperation;
reporter?.Invoke(ratioCompleted, null);
}
#endif
if (resultsMesh != null)
{
var resultsItem = new Object3D()
{
Mesh = resultsMesh
};
resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem);
SourceContainer.Visible = false;
}
}
if (resultsMesh != null)
{
var resultsItem = new Object3D()
{
Mesh = resultsMesh
};
resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem);
SourceContainer.Visible = false;
}
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
}
}

View file

@ -181,7 +181,7 @@ namespace MatterHackers.MatterControl.DesignTools
// first replace spaces with '_'
var name = editSelectedName.Text.Replace(' ', '_');
// next make sure we don't have the exact name already
name = agg_basics.GetNonCollidingName(name, (name) =>
name = Util.GetNonCollidingName(name, (name) =>
{
return !existingNames.Contains(name.ToLower());
}, false);

View file

@ -48,179 +48,175 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[Obsolete("Use SubtractAndReplaceObject3D_2 instead", false)]
[ShowUpdateButton(SuppressPropertyChangeUpdates = true)]
public class SubtractAndReplaceObject3D : MeshWrapperObject3D, ISelectableChildContainer
{
public SubtractAndReplaceObject3D()
{
Name = "Subtract and Replace";
}
[Obsolete("Use SubtractAndReplaceObject3D_2 instead", false)]
[ShowUpdateButton(SuppressPropertyChangeUpdates = true)]
public class SubtractAndReplaceObject3D : MeshWrapperObject3D, ISelectableChildContainer
{
public SubtractAndReplaceObject3D()
{
Name = "Subtract and Replace";
}
public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren();
public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren();
public SelectedChildren SelectedChildren => ItemsToSubtract;
public SelectedChildren SelectedChildren => ItemsToSubtract;
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 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 void SubtractAndReplace()
{
SubtractAndReplace(CancellationToken.None, null);
}
public void SubtractAndReplace()
{
SubtractAndReplace(CancellationToken.None, null);
}
public void SubtractAndReplace(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
public void SubtractAndReplace(CancellationToken cancellationToken, Action<double, string> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
var paintObjects = this.Children
.Where((i) => ItemsToSubtract.Contains(i.ID))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where((i) => !ItemsToSubtract.Contains(i.ID))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
var paintObjects = this.Children
.Where((i) => ItemsToSubtract.Contains(i.ID))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where((i) => !ItemsToSubtract.Contains(i.ID))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
if (paintObjects.Any()
&& keepObjects.Any())
{
var totalOperations = paintObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
if (paintObjects.Any()
&& keepObjects.Any())
{
var totalOperations = paintObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
foreach (var paint in paintObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
var transformedPaint = paint.obj3D.Mesh.Copy(cancellationToken);
transformedPaint.Transform(paint.matrix);
var inverseRemove = paint.matrix.Inverted;
Mesh paintMesh = null;
foreach (var paint in paintObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
var transformedPaint = paint.obj3D.Mesh.Copy(cancellationToken);
transformedPaint.Transform(paint.matrix);
var inverseRemove = paint.matrix.Inverted;
Mesh paintMesh = null;
var progressStatus = new ProgressStatus();
foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
var transformedKeep = keep.obj3D.Mesh.Copy(cancellationToken);
transformedKeep.Transform(keep.matrix);
foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
var transformedKeep = keep.obj3D.Mesh.Copy(cancellationToken);
transformedKeep.Transform(keep.matrix);
// remove the paint from the original
var subtract = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
paint.obj3D.Mesh,
paint.matrix,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
var intersect = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
paint.obj3D.Mesh,
paint.matrix,
CsgModes.Intersect,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
// remove the paint from the original
var subtract = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
paint.obj3D.Mesh,
paint.matrix,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
var inverseKeep = keep.matrix.Inverted;
subtract.Transform(inverseKeep);
using (keep.obj3D.RebuildLock())
{
keep.obj3D.Mesh = subtract;
}
var intersect = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
paint.obj3D.Mesh,
paint.matrix,
CsgModes.Intersect,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
// keep all the intersections together
if (paintMesh == null)
{
paintMesh = intersect;
}
else // union into the current paint
{
paintMesh = BooleanProcessing.Do(paintMesh,
Matrix4X4.Identity,
intersect,
Matrix4X4.Identity,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
}
var inverseKeep = keep.matrix.Inverted;
subtract.Transform(inverseKeep);
using (keep.obj3D.RebuildLock())
{
keep.obj3D.Mesh = subtract;
}
if (cancellationToken.IsCancellationRequested)
{
break;
}
}
// keep all the intersections together
if (paintMesh == null)
{
paintMesh = intersect;
}
else // union into the current paint
{
paintMesh = BooleanProcessing.Do(paintMesh,
Matrix4X4.Identity,
intersect,
Matrix4X4.Identity,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
}
// move the paint mesh back to its original coordinates
paintMesh.Transform(inverseRemove);
if (cancellationToken.IsCancellationRequested)
{
break;
}
}
using (paint.obj3D.RebuildLock())
{
paint.obj3D.Mesh = paintMesh;
}
// move the paint mesh back to its original coordinates
paintMesh.Transform(inverseRemove);
paint.obj3D.Color = paint.obj3D.WorldColor().WithContrast(keepObjects.First().WorldColor(), 2).ToColor();
}
}
}
using (paint.obj3D.RebuildLock())
{
paint.obj3D.Mesh = paintMesh;
}
public override Task Rebuild()
{
var rebuildLocks = this.RebuilLockAll();
paint.obj3D.Color = paint.obj3D.WorldColor().WithContrast(keepObjects.First().WorldColor(), 2).ToColor();
}
}
}
// spin up a task to calculate the paint
return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), null, (reporter, cancellationTokenSource) =>
{
try
{
SubtractAndReplace(cancellationTokenSource.Token, reporter);
}
catch
{
}
public override Task Rebuild()
{
var rebuildLocks = this.RebuilLockAll();
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
// spin up a task to calculate the paint
return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), null, (reporter, cancellationTokenSource) =>
{
try
{
SubtractAndReplace(cancellationTokenSource.Token, reporter);
}
catch
{
}
return Task.CompletedTask;
});
}
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -45,36 +45,36 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[ShowUpdateButton]
public class SubtractAndReplaceObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier
{
public SubtractAndReplaceObject3D_2()
{
Name = "Subtract and Replace";
}
[ShowUpdateButton]
public class SubtractAndReplaceObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier
{
public SubtractAndReplaceObject3D_2()
{
Name = "Subtract and Replace";
}
public bool DoEditorDraw(bool isSelected)
{
return isSelected;
}
public bool DoEditorDraw(bool isSelected)
{
return isSelected;
}
[HideFromEditor]
public SelectedChildren ComputedChildren { get; set; } = new SelectedChildren();
[HideFromEditor]
public SelectedChildren ComputedChildren { get; set; } = new SelectedChildren();
[DisplayName("Part(s) to Subtract and Replace")]
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
[DisplayName("Part(s) to Subtract and Replace")]
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
#if DEBUG
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#else
private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
@ -82,255 +82,247 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#endif
public void AddEditorTransparents(Object3DControlsLayer layer, List<Object3DView> transparentMeshes, DrawEventArgs e)
{
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
public void AddEditorTransparents(Object3DControlsLayer layer, List<Object3DView> transparentMeshes, DrawEventArgs e)
{
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
foreach (var item in removeObjects)
{
var color = item.WorldColor(checkOutputType: true);
transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2)));
}
foreach (var item in removeObjects)
{
var color = item.WorldColor(checkOutputType: true);
transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2)));
}
}
}
}
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
return;
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
return;
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return AxisAlignedBoundingBox.Empty();
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return AxisAlignedBoundingBox.Empty();
}
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 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);
}
}
private CancellationTokenSource cancellationToken;
private CancellationTokenSource cancellationToken;
public bool IsBuilding => this.cancellationToken != null;
public bool IsBuilding => this.cancellationToken != null;
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public override Task Rebuild()
{
var rebuildLocks = this.RebuilLockAll();
public override Task Rebuild()
{
var rebuildLocks = this.RebuilLockAll();
// spin up a task to calculate the paint
return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
try
{
SubtractAndReplace(cancellationTokenSource.Token, reporter);
var newComputedChildren = new SelectedChildren();
// spin up a task to calculate the paint
return ApplicationController.Instance.Tasks.Execute("Replacing".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
try
{
SubtractAndReplace(cancellationTokenSource.Token, reporter);
var newComputedChildren = new SelectedChildren();
foreach (var id in SelectedChildren)
{
newComputedChildren.Add(id);
}
foreach (var id in SelectedChildren)
{
newComputedChildren.Add(id);
}
ComputedChildren = newComputedChildren;
}
catch
{
}
ComputedChildren = newComputedChildren;
}
catch
{
}
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
return Task.CompletedTask;
});
}
public void SubtractAndReplace()
{
SubtractAndReplace(CancellationToken.None, null);
}
public void SubtractAndReplace()
{
SubtractAndReplace(CancellationToken.None, null);
}
private void SubtractAndReplace(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
private void SubtractAndReplace(CancellationToken cancellationToken, Action<double, string> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
if (parentOfPaintTargets.Children.Count() < 2)
{
if (parentOfPaintTargets.Children.Count() == 1)
{
this.Children.Add(SourceContainer.Clone());
SourceContainer.Visible = false;
}
if (parentOfPaintTargets.Children.Count() < 2)
{
if (parentOfPaintTargets.Children.Count() == 1)
{
this.Children.Add(SourceContainer.Clone());
SourceContainer.Visible = false;
}
return;
}
return;
}
SubtractObject3D_2.CleanUpSelectedChildrenIDs(this);
SubtractObject3D_2.CleanUpSelectedChildrenIDs(this);
var paintObjects = parentOfPaintTargets.Children
.Where((i) => SelectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
var paintObjects = parentOfPaintTargets.Children
.Where((i) => SelectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
var keepItems = parentOfPaintTargets.Children
.Where((i) => !SelectedChildren
.Contains(i.ID));
var keepItems = parentOfPaintTargets.Children
.Where((i) => !SelectedChildren
.Contains(i.ID));
var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList();
var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList();
if (paintObjects.Any()
&& keepVisibleItems.Any())
{
var totalOperations = paintObjects.Count * keepVisibleItems.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
if (paintObjects.Any()
&& keepVisibleItems.Any())
{
var totalOperations = paintObjects.Count * keepVisibleItems.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
var progressStatus = new ProgressStatus
{
Status = "Do CSG"
};
foreach (var keep in keepVisibleItems)
{
var keepResultsMesh = keep.Mesh;
var keepWorldMatrix = keep.WorldMatrix(SourceContainer);
foreach (var keep in keepVisibleItems)
{
var keepResultsMesh = keep.Mesh;
var keepWorldMatrix = keep.WorldMatrix(SourceContainer);
foreach (var paint in paintObjects)
{
if (cancellationToken.IsCancellationRequested)
{
SourceContainer.Visible = true;
RemoveAllButSource();
return;
}
foreach (var paint in paintObjects)
{
if (cancellationToken.IsCancellationRequested)
{
SourceContainer.Visible = true;
RemoveAllButSource();
return;
}
Mesh paintMesh = BooleanProcessing.Do(keepResultsMesh,
keepWorldMatrix,
// paint data
paint.Mesh,
paint.WorldMatrix(SourceContainer),
// operation type
CsgModes.Intersect,
Processing,
InputResolution,
OutputResolution,
// reporting data
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
Mesh paintMesh = BooleanProcessing.Do(keepResultsMesh,
keepWorldMatrix,
// paint data
paint.Mesh,
paint.WorldMatrix(SourceContainer),
// operation type
CsgModes.Intersect,
Processing,
InputResolution,
OutputResolution,
// reporting data
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
keepResultsMesh = BooleanProcessing.Do(keepResultsMesh,
keepWorldMatrix,
// point data
paint.Mesh,
paint.WorldMatrix(SourceContainer),
// operation type
CsgModes.Subtract,
Processing,
InputResolution,
OutputResolution,
// reporting data
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
keepResultsMesh = BooleanProcessing.Do(keepResultsMesh,
keepWorldMatrix,
// point data
paint.Mesh,
paint.WorldMatrix(SourceContainer),
// operation type
CsgModes.Subtract,
Processing,
InputResolution,
OutputResolution,
// reporting data
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
// after the first time we get a result the results mesh is in the right coordinate space
keepWorldMatrix = Matrix4X4.Identity;
// after the first time we get a result the results mesh is in the right coordinate space
keepWorldMatrix = Matrix4X4.Identity;
// store our intersection (paint) results mesh
var paintResultsItem = new Object3D()
{
Mesh = paintMesh,
Visible = false,
OwnerID = paint.ID
};
// copy all the properties but the matrix
paintResultsItem.CopyWorldProperties(paint, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
// and add it to this
this.Children.Add(paintResultsItem);
// store our intersection (paint) results mesh
var paintResultsItem = new Object3D()
{
Mesh = paintMesh,
Visible = false,
OwnerID = paint.ID
};
// copy all the properties but the matrix
paintResultsItem.CopyWorldProperties(paint, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
// and add it to this
this.Children.Add(paintResultsItem);
// report our progress
ratioCompleted += amountPerOperation;
reporter?.Invoke(ratioCompleted, "Do CSG".Localize());
}
// report our progress
ratioCompleted += amountPerOperation;
progressStatus.Progress0To1 = ratioCompleted;
reporter?.Report(progressStatus);
}
// store our results mesh
var keepResultsItem = new Object3D()
{
Mesh = keepResultsMesh,
Visible = false,
OwnerID = keep.ID
};
// copy all the properties but the matrix
keepResultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
// and add it to this
this.Children.Add(keepResultsItem);
}
// store our results mesh
var keepResultsItem = new Object3D()
{
Mesh = keepResultsMesh,
Visible = false,
OwnerID = keep.ID
};
// copy all the properties but the matrix
keepResultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
// and add it to this
this.Children.Add(keepResultsItem);
}
foreach (var child in Children)
{
child.Visible = true;
}
foreach (var child in Children)
{
child.Visible = true;
}
SourceContainer.Visible = false;
}
}
SourceContainer.Visible = false;
}
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -46,146 +46,139 @@ using MatterHackers.PolygonMesh.Csg;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[Obsolete("Use SubtractObject3D_2 instead", false)]
[ShowUpdateButton(SuppressPropertyChangeUpdates = true)]
public class SubtractObject3D : MeshWrapperObject3D, ISelectableChildContainer
{
public SubtractObject3D()
{
Name = "Subtract";
}
[Obsolete("Use SubtractObject3D_2 instead", false)]
[ShowUpdateButton(SuppressPropertyChangeUpdates = true)]
public class SubtractObject3D : MeshWrapperObject3D, ISelectableChildContainer
{
public SubtractObject3D()
{
Name = "Subtract";
}
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
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();
}
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();
}
base.OnInvalidate(invalidateType);
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
public void Subtract(CancellationToken cancellationToken, Action<double, string> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
public void Subtract(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
bool ItemInSubtractList(IObject3D item)
{
if (SelectedChildren.Contains(item.ID))
{
return true;
}
bool ItemInSubtractList(IObject3D item)
{
if (SelectedChildren.Contains(item.ID))
{
return true;
}
// check if the wrapped item is in the subtract list
if (item.Children.Count > 0 && SelectedChildren.Contains(item.Children.First().ID))
{
return true;
}
// check if the wrapped item is in the subtract list
if (item.Children.Count > 0 && SelectedChildren.Contains(item.Children.First().ID))
{
return true;
}
return false;
}
return false;
}
var removeObjects = this.Children
.Where(i => ItemInSubtractList(i))
.SelectMany(h => h.DescendantsAndSelf())
.Where(c => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where(i => !ItemInSubtractList(i))
.SelectMany(h => h.DescendantsAndSelf())
.Where(c => c.OwnerID == this.ID).ToList();
var removeObjects = this.Children
.Where(i => ItemInSubtractList(i))
.SelectMany(h => h.DescendantsAndSelf())
.Where(c => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where(i => !ItemInSubtractList(i))
.SelectMany(h => h.DescendantsAndSelf())
.Where(c => c.OwnerID == this.ID).ToList();
if (removeObjects.Any()
&& keepObjects.Any())
{
var totalOperations = removeObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
if (removeObjects.Any()
&& keepObjects.Any())
{
var totalOperations = removeObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
foreach (var remove in removeObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
reporter?.Invoke(0, "Do CSG".Localize());
var result = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
remove.obj3D.Mesh,
remove.matrix,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
cancellationToken);
var inverse = keep.matrix.Inverted;
result.Transform(inverse);
ProgressStatus progressStatus = new ProgressStatus();
foreach (var remove in removeObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
progressStatus.Status = "Do CSG";
reporter?.Report(progressStatus);
var result = BooleanProcessing.Do(keep.obj3D.Mesh,
keep.matrix,
remove.obj3D.Mesh,
remove.matrix,
CsgModes.Subtract,
ProcessingModes.Polygons,
ProcessingResolution._64,
ProcessingResolution._64,
reporter,
amountPerOperation,
ratioCompleted,
progressStatus,
cancellationToken);
var inverse = keep.matrix.Inverted;
result.Transform(inverse);
using (keep.obj3D.RebuildLock())
{
keep.obj3D.Mesh = result;
}
using (keep.obj3D.RebuildLock())
{
keep.obj3D.Mesh = result;
}
ratioCompleted += amountPerOperation;
reporter?.Invoke(ratioCompleted, null);
}
ratioCompleted += amountPerOperation;
progressStatus.Progress0To1 = ratioCompleted;
reporter?.Report(progressStatus);
}
remove.obj3D.Visible = false;
}
}
}
}
remove.obj3D.Visible = false;
}
}
}
}
}

View file

@ -44,29 +44,29 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[ShowUpdateButton]
public class SubtractObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier, IBuildsOnThread
{
public SubtractObject3D_2()
{
Name = "Subtract";
NameOverriden = false;
}
[ShowUpdateButton]
public class SubtractObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier, IBuildsOnThread
{
public SubtractObject3D_2()
{
Name = "Subtract";
NameOverriden = false;
}
[DisplayName("Part(s) to Subtract")]
public virtual SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
[DisplayName("Part(s) to Subtract")]
public virtual SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
#if DEBUG
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
public ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public IplicitSurfaceMethod MeshAnalysis { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#else
private ProcessingModes Processing { get; set; } = ProcessingModes.Polygons;
private ProcessingResolution OutputResolution { get; set; } = ProcessingResolution._64;
@ -74,266 +74,264 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
private ProcessingResolution InputResolution { get; set; } = ProcessingResolution._64;
#endif
public bool RemoveSubtractObjects { get; set; } = true;
public bool RemoveSubtractObjects { get; set; } = true;
public bool DoEditorDraw(bool isSelected)
public bool DoEditorDraw(bool isSelected)
{
return isSelected;
return isSelected;
}
public void AddEditorTransparents(Object3DControlsLayer layer, List<Object3DView> transparentMeshes, DrawEventArgs e)
{
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
public void AddEditorTransparents(Object3DControlsLayer layer, List<Object3DView> transparentMeshes, DrawEventArgs e)
{
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes())
.ToList();
foreach (var item in removeObjects)
{
var color = item.WorldColor(checkOutputType: true);
transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2)));
}
foreach (var item in removeObjects)
{
var color = item.WorldColor(checkOutputType: true);
transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2)));
}
}
}
}
}
public override async void WrapSelectedItemAndSelect(InteractiveScene scene)
{
// this will ask the subtract to do a rebuild
// this will ask the subtract to do a rebuild
base.WrapSelectedItemAndSelect(scene);
if (SelectedChildren.Count == 0)
{
SelectedChildren.Add(SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Last().ID);
}
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
return;
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return AxisAlignedBoundingBox.Empty();
}
public override async void OnInvalidate(InvalidateArgs invalidateArgs)
{
if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateArgs.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this))
{
await Rebuild();
}
else if (SheetObject3D.NeedsRebuild(this, invalidateArgs))
{
await Rebuild();
}
else if(invalidateArgs.InvalidateType.HasFlag(InvalidateType.Name)
&& !NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
else
{
base.OnInvalidate(invalidateArgs);
}
}
private CancellationTokenSource cancellationToken;
public bool IsBuilding => this.cancellationToken != null;
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource;
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
private static (IEnumerable<IObject3D>, IEnumerable<IObject3D>) GetSubtractItems(IObject3D source, SelectedChildren selectedChildren)
{
var parentOfSubtractTargets = source.FirstWithMultipleChildrenDescendantsAndSelf();
// if there are 0 results
if (parentOfSubtractTargets.Children.Count() == 0)
if (SelectedChildren.Count == 0)
{
return (null, null);
SelectedChildren.Add(SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Last().ID);
}
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
return;
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return AxisAlignedBoundingBox.Empty();
}
public override async void OnInvalidate(InvalidateArgs invalidateArgs)
{
if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateArgs.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateArgs.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this))
{
await Rebuild();
}
else if (Expressions.NeedRebuild(this, invalidateArgs))
{
await Rebuild();
}
else if (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Name)
&& !NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
else
{
base.OnInvalidate(invalidateArgs);
}
}
private CancellationTokenSource cancellationToken;
public bool IsBuilding => this.cancellationToken != null;
public void CancelBuild()
{
var threadSafe = this.cancellationToken;
if (threadSafe != null)
{
threadSafe.Cancel();
}
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
this.cancellationToken = cancellationTokenSource;
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
if (!NameOverriden)
{
Name = NameFromChildren();
NameOverriden = false;
}
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
private static (IEnumerable<IObject3D>, IEnumerable<IObject3D>) GetSubtractItems(IObject3D source, SelectedChildren selectedChildren)
{
var parentOfSubtractTargets = source.FirstWithMultipleChildrenDescendantsAndSelf();
// if there are 0 results
if (parentOfSubtractTargets.Children.Count() == 0)
{
return (null, null);
}
// if there is only 1 result (regardless of it being a keep or remove) return it as a keep
if (parentOfSubtractTargets.Children.Count() == 1)
{
return (new IObject3D[] { source }, null);
}
var removeItems = parentOfSubtractTargets.Children
.Where((i) => selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
var keepItems = parentOfSubtractTargets.Children
.Where((i) => !selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
return (keepItems, removeItems);
}
private void Subtract(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
CleanUpSelectedChildrenIDs(this);
var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
var resultItems = DoSubtract(SourceContainer,
keepItems,
removeItems,
reporter,
cancellationToken,
Processing,
InputResolution,
OutputResolution);
foreach(var resultsItem in resultItems)
// if there is only 1 result (regardless of it being a keep or remove) return it as a keep
if (parentOfSubtractTargets.Children.Count() == 1)
{
this.Children.Add(resultsItem);
return (new IObject3D[] { source }, null);
}
if (!RemoveSubtractObjects)
{
this.Children.Modify((list) =>
{
foreach (var item in removeItems)
{
var newObject = new Object3D()
{
Mesh = item.Mesh
};
var removeItems = parentOfSubtractTargets.Children
.Where((i) => selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
newObject.CopyWorldProperties(item, SourceContainer, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Visible));
list.Add(newObject);
}
});
}
}
var keepItems = parentOfSubtractTargets.Children
.Where((i) => !selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
if (Children.Count == 1)
{
// we only have the source item, leave it visible
}
else // hide the source and show the children
{
bool first = true;
foreach (var child in Children)
{
if (first)
{
// hide the source item
child.Visible = false;
first = false;
}
else
{
child.Visible = true;
}
}
}
}
return (keepItems, removeItems);
}
public static void CleanUpSelectedChildrenIDs(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
var parentOfSubtractTargets = item.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
private void Subtract(CancellationToken cancellationToken, Action<double, string> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var allVisibleIDs = parentOfSubtractTargets.Children.Select(i => i.ID);
// remove any names from SelectedChildren that are not a child we can select
foreach (var id in selectableChildContainer.SelectedChildren.ToArray())
{
if (!allVisibleIDs.Contains(id))
{
selectableChildContainer.SelectedChildren.Remove(id);
}
}
}
}
CleanUpSelectedChildrenIDs(this);
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
var resultItems = DoSubtract(SourceContainer,
keepItems,
removeItems,
reporter,
cancellationToken,
Processing,
InputResolution,
OutputResolution);
foreach (var resultsItem in resultItems)
{
this.Children.Add(resultsItem);
if (!RemoveSubtractObjects)
{
this.Children.Modify((list) =>
{
foreach (var item in removeItems)
{
var newObject = new Object3D()
{
Mesh = item.Mesh
};
newObject.CopyWorldProperties(item, SourceContainer, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Visible));
list.Add(newObject);
}
});
}
}
if (Children.Count == 1)
{
// we only have the source item, leave it visible
}
else // hide the source and show the children
{
bool 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 CleanUpSelectedChildrenIDs(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
var parentOfSubtractTargets = item.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var allVisibleIDs = parentOfSubtractTargets.Children.Select(i => i.ID);
// remove any names from SelectedChildren that are not a child we can select
foreach (var id in selectableChildContainer.SelectedChildren.ToArray())
{
if (!allVisibleIDs.Contains(id))
{
selectableChildContainer.SelectedChildren.Remove(id);
}
}
}
}
public void UpdateControls(PublicPropertyChange change)
{
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(OutputResolution), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(MeshAnalysis), () => Processing != ProcessingModes.Polygons);
change.SetRowVisible(nameof(InputResolution), () => Processing != ProcessingModes.Polygons && MeshAnalysis == IplicitSurfaceMethod.Grid);
}
public override string NameFromChildren()
{
var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
return CalculateName(keepItems, ", ", " - ", removeItems, ", ");
}
}
var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
return CalculateName(keepItems, ", ", " - ", removeItems, ", ");
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -27,12 +27,6 @@ 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;
@ -41,221 +35,221 @@ using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.PolygonMesh.Processors;
using MatterHackers.RenderOpenGl;
using MatterHackers.VectorMath;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class SubtractPathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider
{
public SubtractPathObject3D()
{
Name = "Subtract";
}
public class SubtractPathObject3D : OperationSourceContainerObject3D, IEditorDraw, IObject3DControlsProvider, IPrimaryOperationsSpecifier
{
public SubtractPathObject3D()
{
Name = "Subtract";
}
[DisplayName("Part(s) to Subtract")]
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
[DisplayName("Part(s) to Subtract")]
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
this.DrawPath();
}
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
{
this.DrawPath();
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return this.GetWorldspaceAabbOfDrawPath();
}
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
{
return this.GetWorldspaceAabbOfDrawPath();
}
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
object3DControlsLayer.AddControls(ControlTypes.Standard2D);
}
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 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 bool CanApply => true;
public override bool CanApply => true;
public override void Apply(UndoBuffer undoBuffer)
{
this.FlattenToPathObject(undoBuffer);
}
public override void Apply(UndoBuffer undoBuffer)
{
this.FlattenToPathObject(undoBuffer);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationTokenSource) =>
{
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
try
{
Subtract(cancellationTokenSource.Token, reporter);
}
catch
{
}
// set the mesh to show the path
if (this.GetVertexSource() != null)
{
var extrudeMesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight);
if (extrudeMesh.Vertices.Count() > 5)
{
this.Mesh = extrudeMesh;
}
else
{
this.Mesh = null;
}
}
// set the mesh to show the path
if (this.GetVertexSource() != null)
{
var extrudeMesh = this.GetVertexSource().Extrude(Constants.PathPolygonsHeight);
if (extrudeMesh.Vertices.Count() > 5)
{
this.Mesh = extrudeMesh;
}
else
{
this.Mesh = null;
}
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
this.CancelAllParentBuilding();
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
return Task.CompletedTask;
});
}
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
private void Subtract(CancellationToken cancellationToken, Action<double, string> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
private void Subtract(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
if (parentOfSubtractTargets.Children.Count() < 2)
{
if (parentOfSubtractTargets.Children.Count() == 1)
{
this.Children.Add(SourceContainer.Clone());
SourceContainer.Visible = false;
}
if (parentOfSubtractTargets.Children.Count() < 2)
{
if (parentOfSubtractTargets.Children.Count() == 1)
{
this.Children.Add(SourceContainer.Clone());
SourceContainer.Visible = false;
}
return;
}
return;
}
CleanUpSelectedChildrenNames(this);
CleanUpSelectedChildrenNames(this);
var removeVisibleItems = parentOfSubtractTargets.Children
.Where((i) => SelectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisiblePaths())
.ToList();
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 keepItems = parentOfSubtractTargets.Children
.Where((i) => !SelectedChildren
.Contains(i.ID));
var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePaths()).ToList();
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 ratioCompleted = 0;
if (removeVisibleItems.Any()
&& keepVisibleItems.Any())
{
var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count;
double amountPerOperation = 1.0 / totalOperations;
double ratioCompleted = 0;
bool first = true;
foreach (var keep in keepVisibleItems)
{
var resultsVertexSource = keep.GetVertexSource().Transform(keep.Matrix);
var progressStatus = new ProgressStatus
{
Status = "Do Subtract"
};
foreach (var remove in removeVisibleItems)
{
resultsVertexSource = resultsVertexSource.MergePaths(remove.GetVertexSource().Transform(remove.Matrix), ClipperLib.ClipType.ctDifference);
bool first = true;
foreach (var keep in keepVisibleItems)
{
var resultsVertexSource = keep.GetVertexSource().Transform(keep.Matrix);
// report our progress
ratioCompleted += amountPerOperation;
reporter?.Invoke(ratioCompleted, "Do Subtract".Localize());
}
foreach (var remove in removeVisibleItems)
{
resultsVertexSource = resultsVertexSource.MergePaths(remove.GetVertexSource().Transform(remove.Matrix), ClipperLib.ClipType.ctDifference);
if (first)
{
this.VertexStorage = new VertexStorage(resultsVertexSource);
first = false;
}
else
{
this.GetVertexSource().MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion);
}
}
// report our progress
ratioCompleted += amountPerOperation;
progressStatus.Progress0To1 = ratioCompleted;
reporter?.Report(progressStatus);
}
// 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;
}
}
}
}
if (first)
{
this.VertexStorage = new VertexStorage(resultsVertexSource);
first = false;
}
else
{
this.GetVertexSource().MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion);
}
}
public static void CleanUpSelectedChildrenNames(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
var parentOfSubtractTargets = item.FirstWithMultipleChildrenDescendantsAndSelf();
// 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;
}
}
}
}
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);
}
}
}
}
public static void CleanUpSelectedChildrenNames(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
var parentOfSubtractTargets = item.FirstWithMultipleChildrenDescendantsAndSelf();
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);
}
}
}
}
}
public IEnumerable<SceneOperation> GetOperations()
{
return PathObject3D.GetOperations(this.GetType());
}
}
}