Move MatterControl source code into a subdirectory
This commit is contained in:
parent
2c6e34243a
commit
70af2d9ae8
2007 changed files with 13 additions and 8 deletions
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.PolygonMesh;
|
||||
using MatterHackers.PolygonMesh.Csg;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
||||
{
|
||||
[ShowUpdateButton]
|
||||
public class CombineObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread
|
||||
{
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
public CombineObject3D_2()
|
||||
{
|
||||
Name = "Combine";
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
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 IplicitSurfaceMethod MeshAnalysis { get; set; }
|
||||
|
||||
[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 static void CheckManifoldData(CombineObject3D_2 item, IObject3D result)
|
||||
{
|
||||
if (!result.Mesh.IsManifold())
|
||||
{
|
||||
// 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 scene = result.Parents().Last();
|
||||
scene.Children.Add(combine);
|
||||
}
|
||||
}
|
||||
|
||||
public override Task Rebuild()
|
||||
{
|
||||
this.DebugDepth("Rebuild");
|
||||
|
||||
var rebuildLocks = this.RebuilLockAll();
|
||||
|
||||
return ApplicationController.Instance.Tasks.Execute(
|
||||
"Combine".Localize(),
|
||||
null,
|
||||
(reporter, cancellationTokenSource) =>
|
||||
{
|
||||
this.cancellationToken = cancellationTokenSource;
|
||||
reporter?.Invoke(0, null);
|
||||
|
||||
try
|
||||
{
|
||||
Combine(cancellationTokenSource.Token, reporter);
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
SourceContainer.Visible = false;
|
||||
}
|
||||
}
|
||||
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 Combine()
|
||||
{
|
||||
Combine(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
public override string NameFromChildren()
|
||||
{
|
||||
return CalculateName(SourceContainer.Children, " + ");
|
||||
}
|
||||
|
||||
private void Combine(CancellationToken cancellationToken, Action<double, string> reporter)
|
||||
{
|
||||
SourceContainer.Visible = true;
|
||||
RemoveAllButSource();
|
||||
|
||||
var holes = SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(SourceContainer) == PrintOutputTypes.Hole);
|
||||
|
||||
Mesh resultsMesh = null;
|
||||
var participants = SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(SourceContainer) != PrintOutputTypes.Hole);
|
||||
if (participants.Count() == 0)
|
||||
{
|
||||
if (holes.Count() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resultsMesh = Object3D.CombineParticipants(SourceContainer, participants, cancellationToken, reporter, Processing, InputResolution, OutputResolution);
|
||||
}
|
||||
|
||||
var resultsItem = new Object3D()
|
||||
{
|
||||
Mesh = resultsMesh
|
||||
};
|
||||
|
||||
if (holes != null)
|
||||
{
|
||||
var holesMesh = CombineParticipants(SourceContainer, holes, cancellationToken, null);
|
||||
if (holesMesh != null)
|
||||
{
|
||||
var holesItem = new Object3D()
|
||||
{
|
||||
Mesh = holesMesh,
|
||||
OutputType = PrintOutputTypes.Hole
|
||||
};
|
||||
|
||||
if (resultsMesh != null)
|
||||
{
|
||||
var resultItems = SubtractObject3D_2.DoSubtract(this,
|
||||
new List<IObject3D>() { resultsItem },
|
||||
new List<IObject3D>() { holesItem },
|
||||
null,
|
||||
cancellationToken);
|
||||
|
||||
resultsItem.Mesh = resultItems.First().Mesh;
|
||||
}
|
||||
else
|
||||
{
|
||||
holesItem.CopyProperties(holes.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
|
||||
this.Children.Add(holesItem);
|
||||
SourceContainer.Visible = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
|
||||
this.Children.Add(resultsItem);
|
||||
#if DEBUG
|
||||
//resultsItem.Mesh.MergeVertices(.01);
|
||||
//resultsItem.Mesh.CleanAndMerge();
|
||||
//CheckManifoldData(this, resultsItem);
|
||||
#endif
|
||||
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 CancelBuild()
|
||||
{
|
||||
var threadSafe = this.cancellationToken;
|
||||
if (threadSafe != null)
|
||||
{
|
||||
threadSafe.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
Copyright (c) 2018, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace MatterHackers.MatterControl.DesignTools
|
||||
{
|
||||
public class GridWidget : FlowLayoutWidget
|
||||
{
|
||||
public int GridWidth => Children[0].Children.Count;
|
||||
|
||||
public int GridHeight => Children.Count;
|
||||
|
||||
public void SetColumnWidth(int index, double width)
|
||||
{
|
||||
for (int y = 0; y < GridHeight; y++)
|
||||
{
|
||||
Children[y].Children[index].Width = width * GuiWidget.DeviceScale;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRowHeight(int index, double height)
|
||||
{
|
||||
for (int x = 0; x < GridHeight; x++)
|
||||
{
|
||||
Children[index].Children[x].Height = height;
|
||||
}
|
||||
}
|
||||
|
||||
public double GetColumnWidth(int index)
|
||||
{
|
||||
return Children[0].Children[index].Width;
|
||||
}
|
||||
|
||||
public double GetRowHeight(int index)
|
||||
{
|
||||
return Children[index].Children[0].Height;
|
||||
}
|
||||
|
||||
public GridWidget(int gridWidth, int gridHeight, double columnWidth = 60, double rowHeight = 14, ThemeConfig theme = null)
|
||||
: base(FlowDirection.TopToBottom)
|
||||
{
|
||||
for (int y = 0; y < gridHeight; y++)
|
||||
{
|
||||
var row = new FlowLayoutWidget();
|
||||
this.AddChild(row);
|
||||
for (int x = 0; x < gridWidth; x++)
|
||||
{
|
||||
row.AddChild(new GuiWidget()
|
||||
{
|
||||
Width = columnWidth * GuiWidget.DeviceScale,
|
||||
Height = rowHeight * GuiWidget.DeviceScale,
|
||||
Border = 1,
|
||||
BorderColor = theme == null ? Color.LightGray : theme.BorderColor20,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GuiWidget GetCell(int x, int y)
|
||||
{
|
||||
if (x < GridWidth && y < GridHeight)
|
||||
{
|
||||
return Children[y].Children[x];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ExpandRowToMaxHeight(int index)
|
||||
{
|
||||
var maxHeight = GetRowHeight(index);
|
||||
for (int x = 0; x < GridWidth; x++)
|
||||
{
|
||||
var cell = GetCell(x, index).Children.FirstOrDefault();
|
||||
if (cell != null)
|
||||
{
|
||||
maxHeight = Math.Max(maxHeight, cell.Height + cell.DeviceMarginAndBorder.Height);
|
||||
}
|
||||
}
|
||||
|
||||
SetRowHeight(index, maxHeight);
|
||||
}
|
||||
|
||||
public void ExpandColumnToMaxWidth(int index)
|
||||
{
|
||||
var maxWidth = GetColumnWidth(index);
|
||||
for (int y = 0; y < GridHeight; y++)
|
||||
{
|
||||
var cell = GetCell(index, y).Children.FirstOrDefault();
|
||||
if (cell != null)
|
||||
{
|
||||
maxWidth = Math.Max(maxWidth, cell.Width + cell.DeviceMarginAndBorder.Width);
|
||||
}
|
||||
}
|
||||
|
||||
SetColumnWidth(index, maxWidth);
|
||||
}
|
||||
|
||||
public void ExpandToFitContent()
|
||||
{
|
||||
for (int x = 0; x < GridWidth; x++)
|
||||
{
|
||||
ExpandColumnToMaxWidth(x);
|
||||
}
|
||||
|
||||
for (int y = 0; y < GridHeight; y++)
|
||||
{
|
||||
ExpandRowToMaxHeight(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
Copyright (c) 2023, 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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.PolygonMesh.Csg;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
||||
{
|
||||
[ShowUpdateButton]
|
||||
public class IntersectionObject3D_2 : OperationSourceContainerObject3D, IPropertyGridModifier
|
||||
{
|
||||
public IntersectionObject3D_2()
|
||||
{
|
||||
Name = "Intersection";
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
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 IplicitSurfaceMethod MeshAnalysis { get; set; }
|
||||
|
||||
[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
|
||||
|
||||
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(
|
||||
"Intersection".Localize(),
|
||||
null,
|
||||
(reporter, cancellationTokenSource) =>
|
||||
{
|
||||
this.cancellationToken = cancellationTokenSource as CancellationTokenSource;
|
||||
|
||||
try
|
||||
{
|
||||
Intersect(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 override string NameFromChildren()
|
||||
{
|
||||
return CalculateName(SourceContainer.Children, " & ");
|
||||
}
|
||||
|
||||
public void Intersect()
|
||||
{
|
||||
Intersect(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
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 items = participants.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer)));
|
||||
#if false
|
||||
var resultsMesh = BooleanProcessing.DoArray(items,
|
||||
CsgModes.Intersect,
|
||||
Processing,
|
||||
InputResolution,
|
||||
OutputResolution,
|
||||
reporter,
|
||||
cancellationToken);
|
||||
#else
|
||||
var totalOperations = items.Count() - 1;
|
||||
double amountPerOperation = 1.0 / totalOperations;
|
||||
double ratioCompleted = 0;
|
||||
|
||||
var resultsMesh = items.First().Item1;
|
||||
var keepWorldMatrix = items.First().Item2;
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
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 MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.DataConverters3D.UndoCommands;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.PolygonMesh;
|
||||
using MatterHackers.VectorMath;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
||||
{
|
||||
public abstract class MeshWrapperObject3D : Object3D
|
||||
{
|
||||
public MeshWrapperObject3D()
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CanApply => true;
|
||||
|
||||
public override void Apply(UndoBuffer undoBuffer)
|
||||
{
|
||||
using (RebuildLock())
|
||||
{
|
||||
var thisCopy = this.Clone();
|
||||
|
||||
using (thisCopy.RebuilLockAll())
|
||||
{
|
||||
var ownedMeshWrappers = thisCopy.Descendants().Where(o => o.OwnerID == thisCopy.ID).ToList();
|
||||
|
||||
var newMeshObjects = new List<IObject3D>();
|
||||
|
||||
// remove all the meshWrappers (collapse the children)
|
||||
foreach (var ownedMeshWrapper in ownedMeshWrappers)
|
||||
{
|
||||
var wrapperParent = ownedMeshWrapper.Parent;
|
||||
if (ownedMeshWrapper.Visible)
|
||||
{
|
||||
var newMesh = new Object3D()
|
||||
{
|
||||
Mesh = ownedMeshWrapper.Mesh.Copy(CancellationToken.None)
|
||||
};
|
||||
newMesh.CopyProperties(ownedMeshWrapper, Object3DPropertyFlags.All);
|
||||
// move the mesh to the actual new position
|
||||
var matrix = ownedMeshWrapper.WorldMatrix(thisCopy);
|
||||
newMesh.Mesh.Transform(matrix);
|
||||
// then set the matrix to identity
|
||||
newMesh.Matrix = Matrix4X4.Identity;
|
||||
newMesh.Name = thisCopy.Name;
|
||||
newMeshObjects.Add(newMesh);
|
||||
}
|
||||
|
||||
// remove it
|
||||
wrapperParent.Children.Remove(ownedMeshWrapper);
|
||||
}
|
||||
|
||||
thisCopy.Matrix = Matrix4X4.Identity;
|
||||
|
||||
thisCopy.Children.Modify(children =>
|
||||
{
|
||||
children.Clear();
|
||||
children.AddRange(newMeshObjects);
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.MakeNameNonColliding();
|
||||
}
|
||||
});
|
||||
|
||||
List<IObject3D> newChildren = new List<IObject3D>();
|
||||
// push our matrix into a copy of our children
|
||||
foreach (var child in thisCopy.Children)
|
||||
{
|
||||
var newChild = child.Clone();
|
||||
newChildren.Add(newChild);
|
||||
newChild.Matrix *= thisCopy.Matrix;
|
||||
var flags = Object3DPropertyFlags.Visible;
|
||||
if (thisCopy.Color.alpha != 0) flags |= Object3DPropertyFlags.Color;
|
||||
if (thisCopy.OutputType != PrintOutputTypes.Default) flags |= Object3DPropertyFlags.OutputType;
|
||||
if (thisCopy.MaterialIndex != -1) flags |= Object3DPropertyFlags.MaterialIndex;
|
||||
newChild.CopyProperties(thisCopy, flags);
|
||||
}
|
||||
|
||||
// and replace us with the children
|
||||
var replaceCommand = new ReplaceCommand(new List<IObject3D> { this }, newChildren, false);
|
||||
if (undoBuffer != null)
|
||||
{
|
||||
undoBuffer.AddAndDo(replaceCommand);
|
||||
}
|
||||
else
|
||||
{
|
||||
replaceCommand.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(InvalidateType.Children);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MeshWrapperObject3D overrides GetAabb so that it can only check the geometry that it has created
|
||||
/// </summary>
|
||||
/// <param name="matrix"></param>
|
||||
/// <returns></returns>
|
||||
public override AxisAlignedBoundingBox GetAxisAlignedBoundingBox(Matrix4X4 matrix)
|
||||
{
|
||||
AxisAlignedBoundingBox totalBounds = AxisAlignedBoundingBox.Empty();
|
||||
|
||||
// This needs to be Descendants because we need to move past the first visible mesh to our owned objects
|
||||
foreach (var child in this.Descendants().Where(i => i.OwnerID == this.ID && i.Visible))
|
||||
{
|
||||
var childMesh = child.Mesh;
|
||||
if (childMesh != null)
|
||||
{
|
||||
// Add the bounds of each child object
|
||||
var childBounds = childMesh.GetAxisAlignedBoundingBox(child.WorldMatrix(this) * matrix);
|
||||
// Check if the child actually has any bounds
|
||||
if (childBounds.XSize > 0)
|
||||
{
|
||||
totalBounds += childBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalBounds;
|
||||
}
|
||||
|
||||
public IEnumerable<(IObject3D original, IObject3D meshCopy)> WrappedObjects()
|
||||
{
|
||||
return this.Descendants()
|
||||
.Where((obj) => obj.OwnerID == this.ID)
|
||||
.Select((mw) => (mw.Children.First(), mw));
|
||||
}
|
||||
|
||||
public override void Cancel(UndoBuffer undoBuffer)
|
||||
{
|
||||
using (RebuildLock())
|
||||
{
|
||||
var thisClone = this.Clone();
|
||||
|
||||
using (thisClone.RebuildLock())
|
||||
{
|
||||
// remove all the mesh wrappers that we own
|
||||
var meshWrappers = thisClone.Descendants().Where(o => o.OwnerID == thisClone.ID).ToArray();
|
||||
foreach (var meshWrapper in meshWrappers)
|
||||
{
|
||||
meshWrapper.Cancel(null);
|
||||
}
|
||||
foreach (var child in thisClone.Children)
|
||||
{
|
||||
child.OutputType = PrintOutputTypes.Default;
|
||||
|
||||
// push our matrix into a copy of our children (so they don't jump away)
|
||||
using (child.RebuildLock())
|
||||
{
|
||||
child.Matrix *= thisClone.Matrix;
|
||||
}
|
||||
}
|
||||
|
||||
// collapse our children into our parent
|
||||
// and replace us with the children
|
||||
var replaceCommand = new ReplaceCommand(new[] { this }, thisClone.Children.ToList(), false);
|
||||
if (undoBuffer != null)
|
||||
{
|
||||
undoBuffer.AddAndDo(replaceCommand);
|
||||
}
|
||||
else
|
||||
{
|
||||
replaceCommand.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(InvalidateType.Children);
|
||||
}
|
||||
|
||||
public void ResetMeshWrapperMeshes(Object3DPropertyFlags flags, CancellationToken cancellationToken)
|
||||
{
|
||||
using (RebuildLock())
|
||||
{
|
||||
this.DebugDepth("Reset MWM");
|
||||
|
||||
// Remove everything above the objects that have the meshes we are wrapping that are mesh wrappers
|
||||
var wrappers = this.Descendants().Where(o => o.OwnerID == this.ID).ToList();
|
||||
foreach (var wrapper in wrappers)
|
||||
{
|
||||
using (wrapper.RebuildLock())
|
||||
{
|
||||
var remove = wrapper.Parent;
|
||||
while (remove is ModifiedMeshObject3D)
|
||||
{
|
||||
var hold = remove;
|
||||
remove.Cancel(null);
|
||||
remove = hold.Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are not already, wrap all meshes with our id (some inner object may have changed it's meshes)
|
||||
AddMeshWrapperToAllChildren();
|
||||
|
||||
this.Mesh = null;
|
||||
var participants = this.Descendants().Where(o => o.OwnerID == this.ID).ToList();
|
||||
foreach (var item in participants)
|
||||
{
|
||||
var firstChild = item.Children.First();
|
||||
using (item.RebuildLock())
|
||||
{
|
||||
// set the mesh back to a copy of the child mesh
|
||||
item.Mesh = firstChild.Mesh.Copy(cancellationToken);
|
||||
// and reset the properties
|
||||
item.CopyProperties(firstChild, flags & (~Object3DPropertyFlags.Matrix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WrapSelectedItemAndSelect(InteractiveScene scene)
|
||||
{
|
||||
using (RebuildLock())
|
||||
{
|
||||
var selectedItems = scene.GetSelectedItems();
|
||||
|
||||
if (selectedItems.Count > 0)
|
||||
{
|
||||
// clear the selected item
|
||||
scene.SelectedItem = null;
|
||||
|
||||
WrapItems(selectedItems);
|
||||
|
||||
scene.UndoBuffer.AddAndDo(
|
||||
new ReplaceCommand(
|
||||
new List<IObject3D>(selectedItems),
|
||||
new List<IObject3D> { this }));
|
||||
|
||||
// and select this
|
||||
scene.SelectedItem = this;
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(InvalidateType.Properties);
|
||||
}
|
||||
|
||||
public void WrapItems(List<IObject3D> items)
|
||||
{
|
||||
using (RebuildLock())
|
||||
{
|
||||
var clonedItemsToAdd = new List<IObject3D>(items.Select((i) => i.Clone()));
|
||||
|
||||
Children.Modify((list) =>
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
foreach (var child in clonedItemsToAdd)
|
||||
{
|
||||
list.Add(child);
|
||||
}
|
||||
});
|
||||
|
||||
AddMeshWrapperToAllChildren();
|
||||
|
||||
this.MakeNameNonColliding();
|
||||
}
|
||||
|
||||
Invalidate(InvalidateType.Properties);
|
||||
}
|
||||
|
||||
private void AddMeshWrapperToAllChildren()
|
||||
{
|
||||
// Wrap every first descendant that has a mesh
|
||||
foreach (var child in this.VisibleMeshes().ToList())
|
||||
{
|
||||
// have to check that NO child of the visible mesh has us as the parent id
|
||||
if (!child.DescendantsAndSelf().Where((c) => c.OwnerID == this.ID).Any())
|
||||
{
|
||||
// wrap the child
|
||||
child.Parent.Children.Modify((System.Action<List<IObject3D>>)((List<IObject3D> list) =>
|
||||
{
|
||||
list.Remove(child);
|
||||
list.Add((IObject3D)new ModifiedMeshObject3D(child, this.ID));
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
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 MatterHackers.DataConverters3D;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.VectorMath;
|
||||
using System.Linq;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
||||
{
|
||||
/// <summary>
|
||||
/// The goal of MeshWrapper is to provide a mutated version of a source item by some operation. To do so we wrap and clone all
|
||||
/// properties of the source item and reset the source matrix to Identity, given that it now exists on the wrapping parent.
|
||||
/// </summary>
|
||||
public class ModifiedMeshObject3D : Object3D
|
||||
{
|
||||
public ModifiedMeshObject3D()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnInvalidate(InvalidateArgs invalidateType)
|
||||
{
|
||||
var firstChild = this.Children.FirstOrDefault();
|
||||
if (firstChild != null)
|
||||
{
|
||||
if (invalidateType.InvalidateType.HasFlag(InvalidateType.Color))
|
||||
{
|
||||
this.Color = firstChild.Color;
|
||||
}
|
||||
|
||||
if (invalidateType.InvalidateType.HasFlag(InvalidateType.Material))
|
||||
{
|
||||
this.MaterialIndex = firstChild.MaterialIndex;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnInvalidate(invalidateType);
|
||||
}
|
||||
|
||||
public ModifiedMeshObject3D(IObject3D child, string ownerId)
|
||||
{
|
||||
Children.Add(child);
|
||||
|
||||
this.CopyProperties(child, Object3DPropertyFlags.All);
|
||||
this.OwnerID = ownerId;
|
||||
this.Mesh = child.Mesh;
|
||||
|
||||
child.Matrix = Matrix4X4.Identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
Copyright (c) 2018, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.MatterControl.PartPreviewWindow;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using System.Linq;
|
||||
|
||||
namespace MatterHackers.MatterControl.DesignTools
|
||||
{
|
||||
public class SheetEditorWidget : FlowLayoutWidget
|
||||
{
|
||||
public SheetData SheetData { get; private set; }
|
||||
Point2D selectedCell = new Point2D(-1, -1);
|
||||
Dictionary<(int, int), GuiWidget> CellWidgetsByLocation = new Dictionary<(int, int), GuiWidget>();
|
||||
|
||||
public UndoBuffer UndoBuffer { get; }
|
||||
|
||||
private ThemeConfig theme;
|
||||
private ThemedTextEditWidget editSelectedName;
|
||||
public ThemedTextEditWidget EditSelectedExpression { get; private set; }
|
||||
private GridWidget gridWidget;
|
||||
|
||||
public SheetEditorWidget(SheetData sheetData, SheetObject3D sheetObject, UndoBuffer undoBuffer, ThemeConfig theme)
|
||||
: base(FlowDirection.TopToBottom)
|
||||
{
|
||||
this.UndoBuffer = undoBuffer;
|
||||
this.theme = theme;
|
||||
HAnchor = HAnchor.MaxFitOrStretch;
|
||||
|
||||
this.SheetData = sheetData;
|
||||
var cellEditNameWidth = 80 * GuiWidget.DeviceScale;
|
||||
|
||||
// put in the edit row
|
||||
var editSelectionGroup = this.AddChild(new FlowLayoutWidget()
|
||||
{
|
||||
HAnchor = HAnchor.Stretch,
|
||||
VAnchor = VAnchor.Fit,
|
||||
});
|
||||
|
||||
editSelectedName = new ThemedTextEditWidget("", theme, cellEditNameWidth, messageWhenEmptyAndNotSelected: "Name".Localize())
|
||||
{
|
||||
HAnchor = HAnchor.Absolute,
|
||||
};
|
||||
editSelectedName.ActualTextEditWidget.EditComplete += SelectedName_EditComplete;
|
||||
editSelectionGroup.AddChild(editSelectedName);
|
||||
EditSelectedExpression = new ThemedTextEditWidget("", theme, messageWhenEmptyAndNotSelected: "Select cell to edit".Localize())
|
||||
{
|
||||
HAnchor = HAnchor.Stretch,
|
||||
};
|
||||
editSelectionGroup.AddChild(EditSelectedExpression);
|
||||
EditSelectedExpression.ActualTextEditWidget.EditComplete += ActualTextEditWidget_EditComplete1;
|
||||
|
||||
gridWidget = new GridWidget(sheetData.Width + 1, sheetData.Height + 1, theme: theme)
|
||||
{
|
||||
Margin = new BorderDouble(7, 0),
|
||||
};
|
||||
|
||||
this.AddChild(gridWidget);
|
||||
|
||||
for (int x = 0; x < sheetData.Width; x++)
|
||||
{
|
||||
var letterCell = gridWidget.GetCell(x + 1, 0);
|
||||
letterCell.AddChild(new TextWidget(((char)('A' + x)).ToString())
|
||||
{
|
||||
HAnchor = HAnchor.Center,
|
||||
VAnchor = VAnchor.Center,
|
||||
TextColor = theme.TextColor,
|
||||
});
|
||||
|
||||
letterCell.BackgroundColor = theme.SlightShade;
|
||||
}
|
||||
|
||||
gridWidget.SetColumnWidth(0, 20);
|
||||
|
||||
for (int y = 0; y < sheetData.Height; y++)
|
||||
{
|
||||
// add row count
|
||||
var numCell = gridWidget.GetCell(0, y + 1);
|
||||
numCell.AddChild(new TextWidget((y + 1).ToString())
|
||||
{
|
||||
TextColor = theme.TextColor,
|
||||
HAnchor = HAnchor.Center,
|
||||
VAnchor = VAnchor.Center,
|
||||
});
|
||||
|
||||
numCell.BackgroundColor = theme.SlightShade;
|
||||
|
||||
for (int x = 0; x < sheetData.Width; x++)
|
||||
{
|
||||
var capturedX = x;
|
||||
var capturedY = y;
|
||||
|
||||
var edit = new SheetFieldWidget(this, x, y, theme);
|
||||
|
||||
CellWidgetsByLocation.Add((capturedX, capturedY), edit);
|
||||
|
||||
edit.MouseUp += (s, e) => SelectCell(capturedX, capturedY);
|
||||
|
||||
gridWidget.GetCell(x + 1, y + 1).AddChild(edit);
|
||||
}
|
||||
|
||||
gridWidget.ExpandToFitContent();
|
||||
}
|
||||
|
||||
if (sheetObject != null)
|
||||
{
|
||||
PropertyEditor.AddWebPageLinkIfRequired(sheetObject, this, theme);
|
||||
}
|
||||
}
|
||||
|
||||
private void ActualTextEditWidget_EditComplete1(object sender, EventArgs e)
|
||||
{
|
||||
if (selectedCell.x == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SheetData[selectedCell.x, selectedCell.y].Expression = EditSelectedExpression.Text;
|
||||
CellWidgetsByLocation[(selectedCell.x, selectedCell.y)].Text = EditSelectedExpression.Text;
|
||||
SheetData.Recalculate();
|
||||
}
|
||||
|
||||
private void SelectedName_EditComplete(object sender, EventArgs e)
|
||||
{
|
||||
if (selectedCell.x == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existingNames = new HashSet<string>();
|
||||
for (int y = 0; y < SheetData.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < SheetData.Width; x++)
|
||||
{
|
||||
if (x != selectedCell.x || y != selectedCell.y)
|
||||
{
|
||||
var currentName = SheetData[x, y].Name;
|
||||
if (!string.IsNullOrEmpty(currentName))
|
||||
{
|
||||
existingNames.Add(currentName.ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var reservedWords = new string[] { "pi", "e", "true", "false", "round" };
|
||||
|
||||
// add all the reserved words to the existing names
|
||||
foreach (var reservedWord in reservedWords)
|
||||
{
|
||||
existingNames.Add(reservedWord);
|
||||
}
|
||||
|
||||
// first replace spaces with '_'
|
||||
var name = editSelectedName.Text.Replace(' ', '_');
|
||||
// next make sure we don't have the exact name already
|
||||
name = Util.GetNonCollidingName(name, (name) =>
|
||||
{
|
||||
return !existingNames.Contains(name.ToLower());
|
||||
}, false);
|
||||
editSelectedName.Text = name;
|
||||
SheetData[selectedCell.x, selectedCell.y].Name = name;
|
||||
SheetData.Recalculate();
|
||||
}
|
||||
|
||||
public override void OnDraw(Graphics2D graphics2D)
|
||||
{
|
||||
base.OnDraw(graphics2D);
|
||||
|
||||
// draw the selected widget
|
||||
var x = selectedCell.x;
|
||||
var y = selectedCell.y;
|
||||
if (x < 0 || x >= SheetData.Width || y < 0 || y >= SheetData.Height)
|
||||
{
|
||||
// out of bounds
|
||||
return;
|
||||
}
|
||||
|
||||
var cell = gridWidget.GetCell(x + 1, y + 1);
|
||||
var bounds = cell.TransformToParentSpace(this, cell.LocalBounds);
|
||||
graphics2D.Rectangle(bounds, theme.PrimaryAccentColor);
|
||||
}
|
||||
|
||||
public void SelectCell(int x, int y)
|
||||
{
|
||||
if (x < 0 || x >= SheetData.Width || y < 0 || y >= SheetData.Height)
|
||||
{
|
||||
// out of bounds
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCell.x != -1)
|
||||
{
|
||||
CellWidgetsByLocation[(selectedCell.x, selectedCell.y)].BorderColor = Color.Transparent;
|
||||
}
|
||||
selectedCell.x = x;
|
||||
selectedCell.y = y;
|
||||
CellWidgetsByLocation[(selectedCell.x, selectedCell.y)].BorderColor = theme.PrimaryAccentColor;
|
||||
EditSelectedExpression.Text = SheetData[x, y].Expression;
|
||||
if (string.IsNullOrEmpty(SheetData[x, y].Name))
|
||||
{
|
||||
editSelectedName.Text = $"{(char)('A' + x)}{y + 1}";
|
||||
}
|
||||
else
|
||||
{
|
||||
editSelectedName.Text = SheetData[x, y].Name;
|
||||
}
|
||||
|
||||
gridWidget.GetCell(x + 1, y + 1).Children.FirstOrDefault()?.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
public class SheetEditor : IObjectEditor
|
||||
{
|
||||
string IObjectEditor.Name => "Sheet Editor";
|
||||
|
||||
IEnumerable<Type> IObjectEditor.SupportedTypes() => new[] { typeof(SheetObject3D) };
|
||||
|
||||
public GuiWidget Create(object item, UndoBuffer undoBuffer, ThemeConfig theme)
|
||||
{
|
||||
if (item is SheetObject3D sheetObject)
|
||||
{
|
||||
return new SheetEditorWidget(sheetObject.SheetData, sheetObject, undoBuffer, theme);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
Copyright (c) 2018, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using MatterHackers.Agg.UI;
|
||||
|
||||
namespace MatterHackers.MatterControl.DesignTools
|
||||
{
|
||||
public class SheetFieldWidget : GuiWidget
|
||||
{
|
||||
private SheetEditorWidget sheetEditorWidget;
|
||||
private int x;
|
||||
private int y;
|
||||
private TextWidget content;
|
||||
private string undoContent;
|
||||
|
||||
private SheetData SheetData => sheetEditorWidget.SheetData;
|
||||
|
||||
private enum EditModes
|
||||
{
|
||||
Unknown,
|
||||
QuickText,
|
||||
FullEdit
|
||||
}
|
||||
|
||||
private EditModes EditMode { get; set; }
|
||||
|
||||
public SheetFieldWidget(SheetEditorWidget sheetEditorWidget, int x, int y, ThemeConfig theme)
|
||||
{
|
||||
this.sheetEditorWidget = sheetEditorWidget;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.Name = $"Cell {x},{y}";
|
||||
|
||||
HAnchor = HAnchor.Stretch;
|
||||
VAnchor = VAnchor.Stretch;
|
||||
Selectable = true;
|
||||
|
||||
content = new TextWidget("")
|
||||
{
|
||||
TextColor = theme.TextColor,
|
||||
AutoExpandBoundsToText = true,
|
||||
VAnchor = VAnchor.Bottom,
|
||||
};
|
||||
|
||||
SheetData.Recalculated += (s, e) =>
|
||||
{
|
||||
UpdateContents();
|
||||
};
|
||||
|
||||
UpdateContents();
|
||||
|
||||
this.AddChild(content);
|
||||
}
|
||||
|
||||
private void UpdateContents()
|
||||
{
|
||||
content.Text = SheetData.GetCellValue(SheetData.CellId(x, y));
|
||||
}
|
||||
|
||||
public override void OnKeyPress(KeyPressEventArgs keyPressEvent)
|
||||
{
|
||||
// this must be called first to ensure we get the correct Handled state
|
||||
base.OnKeyPress(keyPressEvent);
|
||||
if (!keyPressEvent.Handled)
|
||||
{
|
||||
if (keyPressEvent.KeyChar < 32
|
||||
&& keyPressEvent.KeyChar != 13
|
||||
&& keyPressEvent.KeyChar != 9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditMode == EditModes.Unknown)
|
||||
{
|
||||
EditMode = EditModes.QuickText;
|
||||
this.content.Text = "";
|
||||
}
|
||||
|
||||
this.content.Text += keyPressEvent.KeyChar.ToString();
|
||||
|
||||
keyPressEvent.Handled = true;
|
||||
|
||||
UpdateSheetEditField();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSheetEditField()
|
||||
{
|
||||
this.sheetEditorWidget.EditSelectedExpression.Text = this.content.Text;
|
||||
}
|
||||
|
||||
public override void OnKeyDown(KeyEventArgs keyEvent)
|
||||
{
|
||||
// handle the tab early so it does not get consumed with switching to the next widget
|
||||
if (!keyEvent.Handled)
|
||||
{
|
||||
switch (keyEvent.KeyCode)
|
||||
{
|
||||
case Keys.Tab:
|
||||
Navigate(1, 0);
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this must be called first to ensure we get the correct Handled state
|
||||
base.OnKeyDown(keyEvent);
|
||||
|
||||
if (!keyEvent.Handled)
|
||||
{
|
||||
switch (keyEvent.KeyCode)
|
||||
{
|
||||
case Keys.Escape:
|
||||
// reset the selction to what it was before we started editing
|
||||
this.content.Text = undoContent;
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
// and go back to the unknown state
|
||||
EditMode = EditModes.Unknown;
|
||||
UpdateSheetEditField();
|
||||
break;
|
||||
|
||||
case Keys.Left:
|
||||
Navigate(-1, 0);
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
|
||||
case Keys.Down:
|
||||
Navigate(0, 1);
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
|
||||
case Keys.Right:
|
||||
Navigate(1, 0);
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
|
||||
case Keys.Up:
|
||||
Navigate(0, -1);
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
|
||||
case Keys.Enter:
|
||||
switch (EditMode)
|
||||
{
|
||||
// go into full edit
|
||||
case EditModes.Unknown:
|
||||
EditMode = EditModes.FullEdit;
|
||||
break;
|
||||
|
||||
// finish edit and move down
|
||||
case EditModes.QuickText:
|
||||
case EditModes.FullEdit:
|
||||
Navigate(0, 1);
|
||||
// make sure we know we are edit complete
|
||||
EditComplete();
|
||||
break;
|
||||
}
|
||||
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
break;
|
||||
|
||||
case Keys.Delete:
|
||||
switch (EditMode)
|
||||
{
|
||||
case EditModes.Unknown:
|
||||
// delete content
|
||||
this.content.Text = "";
|
||||
break;
|
||||
|
||||
case EditModes.QuickText:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
case EditModes.FullEdit:
|
||||
// delete from front
|
||||
break;
|
||||
}
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
UpdateSheetEditField();
|
||||
break;
|
||||
|
||||
case Keys.Back:
|
||||
switch (EditMode)
|
||||
{
|
||||
case EditModes.Unknown:
|
||||
// delete text
|
||||
this.content.Text = "";
|
||||
break;
|
||||
|
||||
case EditModes.QuickText:
|
||||
// delete from back
|
||||
if (this.content.Text.Length > 0)
|
||||
{
|
||||
this.content.Text = this.content.Text.Substring(0, this.content.Text.Length - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case EditModes.FullEdit:
|
||||
// delete from back
|
||||
break;
|
||||
}
|
||||
|
||||
keyEvent.SuppressKeyPress = true;
|
||||
keyEvent.Handled = true;
|
||||
UpdateSheetEditField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFocusChanged(System.EventArgs e)
|
||||
{
|
||||
base.OnFocusChanged(e);
|
||||
|
||||
if (this.Focused)
|
||||
{
|
||||
undoContent = this.content.Text;
|
||||
EditMode = EditModes.Unknown;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private void EditComplete()
|
||||
{
|
||||
if (this.content.Text != undoContent)
|
||||
{
|
||||
var newValue = this.content.Text;
|
||||
var oldValue = SheetData[x, y].Expression;
|
||||
// this needs to support undo buffer
|
||||
sheetEditorWidget.UndoBuffer.AddAndDo(new UndoRedoActions(() =>
|
||||
{
|
||||
this.content.Text = oldValue;
|
||||
// make sure the is a sheet update
|
||||
SheetData[x, y].Expression = this.content.Text;
|
||||
SheetData.Recalculate();
|
||||
undoContent = this.content.Text;
|
||||
},
|
||||
() =>
|
||||
{
|
||||
this.content.Text = newValue;
|
||||
// make sure the is a sheet update
|
||||
SheetData[x, y].Expression = this.content.Text;
|
||||
SheetData.Recalculate();
|
||||
undoContent = this.content.Text;
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
EditMode = EditModes.Unknown;
|
||||
}
|
||||
|
||||
private void Navigate(int xOffset, int yOffset)
|
||||
{
|
||||
sheetEditorWidget.SelectCell(x + xOffset, y + yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/**************************** OBSOLETE! ******************************/
|
||||
/************************ USE NEWER VERSION **************************/
|
||||
/*********************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.PolygonMesh;
|
||||
using MatterHackers.PolygonMesh.Csg;
|
||||
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";
|
||||
}
|
||||
|
||||
public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren();
|
||||
|
||||
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 void SubtractAndReplace()
|
||||
{
|
||||
SubtractAndReplace(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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 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,
|
||||
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,
|
||||
cancellationToken);
|
||||
|
||||
var inverseKeep = keep.matrix.Inverted;
|
||||
subtract.Transform(inverseKeep);
|
||||
using (keep.obj3D.RebuildLock())
|
||||
{
|
||||
keep.obj3D.Mesh = subtract;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// move the paint mesh back to its original coordinates
|
||||
paintMesh.Transform(inverseRemove);
|
||||
|
||||
using (paint.obj3D.RebuildLock())
|
||||
{
|
||||
paint.obj3D.Mesh = paintMesh;
|
||||
}
|
||||
|
||||
paint.obj3D.Color = paint.obj3D.WorldColor().WithContrast(keepObjects.First().WorldColor(), 2).ToColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SubtractAndReplace(cancellationTokenSource.Token, reporter);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
rebuildLocks.Dispose();
|
||||
this.CancelAllParentBuilding();
|
||||
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
Copyright (c) 2023, 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.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.PolygonMesh;
|
||||
using MatterHackers.PolygonMesh.Csg;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
|
||||
{
|
||||
[ShowUpdateButton]
|
||||
public class SubtractAndReplaceObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer, ICustomEditorDraw, IPropertyGridModifier
|
||||
{
|
||||
public SubtractAndReplaceObject3D_2()
|
||||
{
|
||||
Name = "Subtract and Replace";
|
||||
}
|
||||
|
||||
public bool DoEditorDraw(bool isSelected)
|
||||
{
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
[HideFromEditor]
|
||||
public SelectedChildren ComputedChildren { 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;
|
||||
|
||||
[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 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 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();
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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();
|
||||
|
||||
foreach (var id in SelectedChildren)
|
||||
{
|
||||
newComputedChildren.Add(id);
|
||||
}
|
||||
|
||||
ComputedChildren = newComputedChildren;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
this.cancellationToken = null;
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
rebuildLocks.Dispose();
|
||||
this.CancelAllParentBuilding();
|
||||
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public void SubtractAndReplace()
|
||||
{
|
||||
SubtractAndReplace(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
private void SubtractAndReplace(CancellationToken cancellationToken, Action<double, string> reporter)
|
||||
{
|
||||
SourceContainer.Visible = true;
|
||||
RemoveAllButSource();
|
||||
|
||||
var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
|
||||
|
||||
if (parentOfPaintTargets.Children.Count() < 2)
|
||||
{
|
||||
if (parentOfPaintTargets.Children.Count() == 1)
|
||||
{
|
||||
this.Children.Add(SourceContainer.Clone());
|
||||
SourceContainer.Visible = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SubtractObject3D_2.CleanUpSelectedChildrenIDs(this);
|
||||
|
||||
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 keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList();
|
||||
|
||||
if (paintObjects.Any()
|
||||
&& keepVisibleItems.Any())
|
||||
{
|
||||
var totalOperations = paintObjects.Count * keepVisibleItems.Count * 2;
|
||||
double amountPerOperation = 1.0 / totalOperations;
|
||||
double ratioCompleted = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ratioCompleted += amountPerOperation;
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// report our progress
|
||||
ratioCompleted += amountPerOperation;
|
||||
reporter?.Invoke(ratioCompleted, "Do CSG".Localize());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
/*********************************************************************/
|
||||
/**************************** OBSOLETE! ******************************/
|
||||
/************************ USE NEWER VERSION **************************/
|
||||
/*********************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.PolygonMesh;
|
||||
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";
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
base.OnInvalidate(invalidateType);
|
||||
}
|
||||
|
||||
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) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Subtract(cancellationTokenSource.Token, reporter);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
rebuildLocks.Dispose();
|
||||
this.CancelAllParentBuilding();
|
||||
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public void Subtract()
|
||||
{
|
||||
Subtract(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
public void Subtract(CancellationToken cancellationToken, Action<double, string> reporter)
|
||||
{
|
||||
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
using (keep.obj3D.RebuildLock())
|
||||
{
|
||||
keep.obj3D.Mesh = result;
|
||||
}
|
||||
|
||||
ratioCompleted += amountPerOperation;
|
||||
reporter?.Invoke(ratioCompleted, null);
|
||||
}
|
||||
|
||||
remove.obj3D.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
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.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.PolygonMesh.Csg;
|
||||
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;
|
||||
}
|
||||
|
||||
[DisplayName("Part(s) to Subtract")]
|
||||
public virtual SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
|
||||
|
||||
#if DEBUG
|
||||
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 IplicitSurfaceMethod MeshAnalysis { get; set; }
|
||||
|
||||
[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 RemoveSubtractObjects { get; set; } = true;
|
||||
|
||||
public bool DoEditorDraw(bool 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();
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override async void WrapSelectedItemAndSelect(InteractiveScene scene)
|
||||
{
|
||||
// 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 (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, Action<double, string> 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)
|
||||
{
|
||||
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, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Copyright (c) 2023, 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 MatterControlLib.DesignTools.Operations.Path;
|
||||
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.PolygonMesh.Processors;
|
||||
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, IPrimaryOperationsSpecifier, IPathProvider
|
||||
{
|
||||
public SubtractPathObject3D()
|
||||
{
|
||||
Name = "Subtract";
|
||||
}
|
||||
|
||||
[DisplayName("Part(s) to Subtract")]
|
||||
public SelectedChildren SelectedChildren { get; set; } = new SelectedChildren();
|
||||
|
||||
public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e)
|
||||
{
|
||||
this.DrawPath();
|
||||
}
|
||||
|
||||
public AxisAlignedBoundingBox GetEditorWorldspaceAABB(Object3DControlsLayer layer)
|
||||
{
|
||||
return this.GetWorldspaceAabbOfDrawPath();
|
||||
}
|
||||
|
||||
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 bool CanApply => true;
|
||||
|
||||
public bool MeshIsSolidObject => false;
|
||||
|
||||
public VertexStorage VertexStorage { get; set; }
|
||||
|
||||
public override void Apply(UndoBuffer undoBuffer)
|
||||
{
|
||||
this.FlattenToPathObject(undoBuffer);
|
||||
}
|
||||
|
||||
public override Task Rebuild()
|
||||
{
|
||||
this.DebugDepth("Rebuild");
|
||||
|
||||
var rebuildLocks = this.RebuilLockAll();
|
||||
|
||||
return ApplicationController.Instance.Tasks.Execute(
|
||||
"Subtract".Localize(),
|
||||
null,
|
||||
(reporter, cancellationTokenSource) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Subtract(cancellationTokenSource.Token, reporter);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// set the mesh to show the path
|
||||
if (this.GetRawPath() != null)
|
||||
{
|
||||
var extrudeMesh = this.GetRawPath().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));
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public void Subtract()
|
||||
{
|
||||
Subtract(CancellationToken.None, null);
|
||||
}
|
||||
|
||||
private void Subtract(CancellationToken cancellationToken, Action<double, string> reporter)
|
||||
{
|
||||
SourceContainer.Visible = true;
|
||||
RemoveAllButSource();
|
||||
|
||||
var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
|
||||
|
||||
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.VisiblePathProviders())
|
||||
.ToList();
|
||||
|
||||
var keepItems = parentOfSubtractTargets.Children
|
||||
.Where((i) => !SelectedChildren
|
||||
.Contains(i.ID));
|
||||
|
||||
var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePathProviders()).ToList();
|
||||
|
||||
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 keepObject3D = keep as Object3D;
|
||||
var resultsVertexSource = keep.GetTransformedPath(this);
|
||||
|
||||
foreach (var remove in removeVisibleItems)
|
||||
{
|
||||
resultsVertexSource = resultsVertexSource.MergePaths(remove.GetTransformedPath(this), ClipperLib.ClipType.ctDifference);
|
||||
|
||||
// report our progress
|
||||
ratioCompleted += amountPerOperation;
|
||||
reporter?.Invoke(ratioCompleted, "Do Subtract".Localize());
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
this.VertexStorage = new VertexStorage(resultsVertexSource);
|
||||
this.CopyProperties(keepObject3D, Object3DPropertyFlags.Color);
|
||||
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GetRawPath().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.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 PathObject3DAbstract.GetOperations(this.GetType());
|
||||
}
|
||||
|
||||
public IVertexSource GetRawPath()
|
||||
{
|
||||
return VertexStorage;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue