Improving curve tool and subtract tool

Added world render cylinder function
Make curve tool default to adding a curve
This commit is contained in:
Lars Brubaker 2018-05-24 10:25:40 -07:00
parent 95657edce0
commit dfb1e6d498
14 changed files with 314 additions and 176 deletions

View file

@ -512,7 +512,7 @@ namespace MatterHackers.MatterControl
{
OperationType = typeof(CombineObject3D),
TitleResolver = () => "Combine".Localize(),
Action = (scene) => MeshWrapperObject3D.WrapSelection(new CombineObject3D(), scene),
Action = (scene) => new CombineObject3D().DoInitialWrapping(scene),
Icon = AggContext.StaticData.LoadIcon("combine.png").SetPreMultiply(),
IsEnabled = (scene) => scene.SelectedItem is SelectionGroup,
},
@ -520,7 +520,7 @@ namespace MatterHackers.MatterControl
{
OperationType = typeof(SubtractObject3D),
TitleResolver = () => "Subtract".Localize(),
Action = (scene) => MeshWrapperObject3D.WrapSelection(new SubtractObject3D(), scene),
Action = (scene) => new SubtractObject3D().DoInitialWrapping(scene),
Icon = AggContext.StaticData.LoadIcon("subtract.png").SetPreMultiply(),
IsEnabled = (scene) => scene.SelectedItem is SelectionGroup,
},
@ -528,7 +528,7 @@ namespace MatterHackers.MatterControl
{
OperationType = typeof(IntersectionObject3D),
TitleResolver = () => "Intersect".Localize(),
Action = (scene) => MeshWrapperObject3D.WrapSelection(new IntersectionObject3D(), scene),
Action = (scene) => new IntersectionObject3D().DoInitialWrapping(scene),
Icon = AggContext.StaticData.LoadIcon("intersect.png"),
IsEnabled = (scene) => scene.SelectedItem is SelectionGroup,
},
@ -536,7 +536,7 @@ namespace MatterHackers.MatterControl
{
OperationType = typeof(SubtractAndReplaceObject3D),
TitleResolver = () => "Subtract & Replace".Localize(),
Action = (scene) => MeshWrapperObject3D.WrapSelection(new SubtractAndReplaceObject3D(), scene),
Action = (scene) => new SubtractAndReplaceObject3D().DoInitialWrapping(scene),
Icon = AggContext.StaticData.LoadIcon("subtract_and_replace.png").SetPreMultiply(),
IsEnabled = (scene) => scene.SelectedItem is SelectionGroup,
},
@ -594,7 +594,7 @@ namespace MatterHackers.MatterControl
Action = (scene) =>
{
var pinch = new PinchObject3D();
MeshWrapperObject3D.WrapSelection(pinch, scene);
pinch.DoInitialWrapping(scene);
},
Icon = AggContext.StaticData.LoadIcon("pinch.png", 16, 16, theme.InvertIcons),
IsEnabled = (scene) => scene.HasSelection,
@ -606,7 +606,7 @@ namespace MatterHackers.MatterControl
Action = (scene) =>
{
var curve = new CurveObject3D();
MeshWrapperObject3D.WrapSelection(curve, scene);
curve.DoInitialWrapping(scene);
},
Icon = AggContext.StaticData.LoadIcon("curve.png", 16, 16, theme.InvertIcons),
IsEnabled = (scene) => scene.HasSelection,

View file

@ -31,21 +31,28 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using MatterHackers.MeshVisualizer;
using MatterHackers.PolygonMesh;
using MatterHackers.RenderOpenGl.OpenGl;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.DesignTools
{
public class CurveObject3D : MeshWrapperObject3D, IPublicPropertyObject
public class CurveObject3D : MeshWrapperObject3D, IPublicPropertyObject, IEditorDraw
{
public double Diameter { get; set; } = 0;
public double Diameter { get; set; } = double.MinValue;
[DisplayName("Bend Up")]
public bool BendCcw { get; set; } = true;
// holds where we rotate the object
Vector2 rotationCenter;
public CurveObject3D()
{
}
@ -53,7 +60,7 @@ namespace MatterHackers.MatterControl.DesignTools
public override void Rebuild(UndoBuffer undoBuffer)
{
Rebuilding = true;
ResetMeshWrappers();
ResetMeshWrappers(Object3DPropertyFlags.All);
// remember the current matrix then clear it so the parts will rotate at the original wrapped position
var currentMatrix = Matrix;
@ -62,13 +69,21 @@ namespace MatterHackers.MatterControl.DesignTools
var meshWrapper = this.Descendants()
.Where((obj) => obj.OwnerID == this.ID).ToList();
var meshWrapperEnumerator = meshWrapper.Select((mw) => (Original: mw.Children.First(), Curved: mw));
bool allMeshesAreValid = true;
// reset the positions before we take the aabb
foreach (var items in meshWrapper.Select((mw) => (Original: mw.Children.First(),
Transformed: mw)))
foreach (var items in meshWrapperEnumerator)
{
var transformedMesh = items.Transformed.Mesh;
var transformedMesh = items.Curved.Mesh;
var originalMesh = items.Original.Mesh;
if(transformedMesh == null || originalMesh == null)
{
allMeshesAreValid = false;
break;
}
for (int i = 0; i < transformedMesh.Vertices.Count; i++)
{
transformedMesh.Vertices[i].Position = originalMesh.Vertices[i].Position;
@ -79,12 +94,19 @@ namespace MatterHackers.MatterControl.DesignTools
var aabb = this.GetAxisAlignedBoundingBox();
if (Diameter > 0)
if (Diameter == double.MinValue)
{
// uninitialized set to a reasonable value
Diameter = (int)aabb.XSize;
}
if (Diameter > 0
&& allMeshesAreValid)
{
var radius = Diameter / 2;
var circumference = MathHelper.Tau * radius;
var rotationCenter = new Vector2(aabb.minXYZ.X, aabb.maxXYZ.Y + radius);
foreach (var object3Ds in meshWrapper.Select((mw) => (Original: mw.Children.First(), Curved: mw)))
rotationCenter = new Vector2(aabb.minXYZ.X, aabb.maxXYZ.Y + radius);
foreach (var object3Ds in meshWrapperEnumerator)
{
// split edges to make it curve better
/*if(false)
@ -153,6 +175,12 @@ namespace MatterHackers.MatterControl.DesignTools
cuvedMesh.MarkAsChanged();
cuvedMesh.CalculateNormals();
}
if (!BendCcw)
{
// fix the stored center so we draw correctly
rotationCenter = new Vector2(aabb.minXYZ.X, aabb.minXYZ.Y - radius);
}
}
// set the matrix back
@ -172,5 +200,22 @@ namespace MatterHackers.MatterControl.DesignTools
}
base.OnInvalidate(invalidateType);
}
public void DrawEditor(object sender, DrawEventArgs e)
{
if (sender is InteractionLayer layer
&& layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem.DescendantsAndSelf().Where((i) => i == this).Any())
{
// we want to measure the
var currentMatrixInv = Matrix.GetInverted();
var aabb = this.GetAxisAlignedBoundingBox(currentMatrixInv);
layer.World.RenderCylinderOutline(this.WorldMatrix(), new Vector3(rotationCenter, aabb.Center.Z), Diameter, aabb.ZSize, 30, Color.Red);
}
// turn the lighting back on
GL.Enable(EnableCap.Lighting);
}
}
}

View file

@ -198,9 +198,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
&& layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem.DescendantsAndSelf().Where((i) => i == this).Any())
{
var aabb = ItemToScale.GetAxisAlignedBoundingBox();
if (FitType == FitType.Box)
{
var aabb = ItemToScale.GetAxisAlignedBoundingBox();
var center = aabb.Center;
var worldMatrix = this.WorldMatrix();
@ -214,45 +215,13 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
else
{
RenderCylinderOutline(layer.World, Color.Red, 1, 1);
layer.World.RenderCylinderOutline(this.WorldMatrix(), aabb.Center, Diameter, Height, 30, Color.Red, 1, 1);
}
// turn the lighting back on
GL.Enable(EnableCap.Lighting);
}
}
public void RenderCylinderOutline(WorldView world, Color color, double lineWidth, double extendLineLength)
{
var aabb = ItemToScale.GetAxisAlignedBoundingBox();
var center = aabb.Center;
center.Z = 0;
var worldMatrix = this.WorldMatrix();
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
int sides = 30;
for (int i=0; i<sides; i++)
{
var rotatedPoint = new Vector3(Math.Cos(MathHelper.Tau * i / sides), Math.Sin(MathHelper.Tau * i / sides), 0) * Diameter / 2;
var sideTop = Vector3.Transform(center + rotatedPoint + new Vector3(0, 0, aabb.minXYZ.Z + Height), worldMatrix);
var sideBottom = Vector3.Transform(center + rotatedPoint + new Vector3(0, 0, aabb.minXYZ.Z), worldMatrix);
var rotated2Point = new Vector3(Math.Cos(MathHelper.Tau * (i + 1) / sides), Math.Sin(MathHelper.Tau * (i + 1) / sides), 0) * Diameter / 2;
var topStart = sideTop;
var topEnd = Vector3.Transform(center + rotated2Point + new Vector3(0, 0, aabb.minXYZ.Z + Height), worldMatrix);
var bottomStart = sideBottom;
var bottomEnd = Vector3.Transform(center + rotated2Point + new Vector3(0, 0, aabb.minXYZ.Z), worldMatrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideTop, ref sideBottom, extendLineLength);
}
world.Render3DLineNoPrep(frustum, sideTop, sideBottom, color, lineWidth);
world.Render3DLineNoPrep(frustum, topStart, topEnd, color, lineWidth);
world.Render3DLineNoPrep(frustum, bottomStart, bottomEnd, color, lineWidth);
}
}
public void UpdateControls(PPEContext context)
{
context.GetEditRow(nameof(Diameter)).Visible = FitType != FitType.Box;

View file

@ -47,14 +47,32 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
return object3D.Parent == null;
}
public static void CopyProperties(this IObject3D copyTo, IObject3D copyFrom)
public static void CopyProperties(this IObject3D copyTo, IObject3D copyFrom, Object3DPropertyFlags flags)
{
copyTo.Matrix = copyFrom.Matrix;
copyTo.Color = copyFrom.Color;
copyTo.MaterialIndex = copyFrom.MaterialIndex;
copyTo.Name = copyFrom.Name;
copyTo.OutputType = copyFrom.OutputType;
copyTo.Visible = copyFrom.Visible;
if (flags.HasFlag(Object3DPropertyFlags.Matrix))
{
copyTo.Matrix = copyFrom.Matrix;
}
if (flags.HasFlag(Object3DPropertyFlags.Color))
{
copyTo.Color = copyFrom.Color;
}
if (flags.HasFlag(Object3DPropertyFlags.MaterialIndex))
{
copyTo.MaterialIndex = copyFrom.MaterialIndex;
}
if (flags.HasFlag(Object3DPropertyFlags.Name))
{
copyTo.Name = copyFrom.Name;
}
if (flags.HasFlag(Object3DPropertyFlags.OutputType))
{
copyTo.OutputType = copyFrom.OutputType;
}
if (flags.HasFlag(Object3DPropertyFlags.Visible))
{
copyTo.Visible = copyFrom.Visible;
}
}
public static IObject3D Translate(this IObject3D objectToTranslate, double x = 0, double y = 0, double z = 0, string name = "")
@ -83,7 +101,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
public static IObject3D Minus(this IObject3D a, IObject3D b)
{
var resultsA = a.Clone();
SubtractEditor.Subtract(resultsA.VisibleMeshes().Select((i) => i.object3D).ToList(), b.VisibleMeshes().Select((i) => i.object3D).ToList());
SubtractObject3D.Subtract(resultsA.VisibleMeshes().Select((i) => i.object3D).ToList(), b.VisibleMeshes().Select((i) => i.object3D).ToList());
return resultsA;
}

View file

@ -50,7 +50,7 @@ namespace MatterHackers.MatterControl.DesignTools
public override void Rebuild(UndoBuffer undoBuffer)
{
Rebuilding = true;
ResetMeshWrappers();
ResetMeshWrappers(Object3DPropertyFlags.All);
// remember the current matrix then clear it so the parts will rotate at the original wrapped position
var currentMatrix = Matrix;

View file

@ -77,7 +77,7 @@ namespace MatterHackers.MatterControl.DesignTools
{
// change this from a text object to a group
var newContainer = new Object3D();
newContainer.CopyProperties(this);
newContainer.CopyProperties(this, Object3DPropertyFlags.All);
foreach (var child in this.Children)
{
newContainer.Children.Add(child.Clone());

View file

@ -101,6 +101,7 @@
<Compile Include="PartPreviewWindow\GCodeDetails\IToggleOption.cs" />
<Compile Include="PartPreviewWindow\LeftClipFlowLayoutWidget.cs" />
<Compile Include="PartPreviewWindow\Object3DTreeBuilder.cs" />
<Compile Include="PartPreviewWindow\View3D\Actions\SubtractObject3D.cs" />
<Compile Include="PrinterControls\ControlWidgets\CalibrationControls.cs" />
<Compile Include="ConfigurationPage\PrintLeveling\LevelingFunctions.cs" />
<Compile Include="ConfigurationPage\PrintLeveling\ProbeCalibrationWizard.cs" />

View file

@ -28,6 +28,7 @@ either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
@ -46,14 +47,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
Children.Add(child);
this.Name = child.Name;
this.CopyProperties(child, Object3DPropertyFlags.All);
this.OwnerID = ownerId;
this.MaterialIndex = child.MaterialIndex;
this.OutputType = child.OutputType;
this.Color = child.Color;
this.Mesh = child.Mesh;
this.Matrix = child.Matrix;
child.Matrix = Matrix4X4.Identity;
}
}

View file

@ -90,8 +90,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
base.Apply(undoBuffer);
}
public static void WrapSelection(MeshWrapperObject3D meshWrapper, InteractiveScene scene)
public void DoInitialWrapping(InteractiveScene scene)
{
MeshWrapperObject3D meshWrapper = this;
Rebuilding = true;
var selectedItem = scene.SelectedItem;
if (selectedItem != null)
{
@ -119,6 +122,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
meshWrapper.MakeNameNonColliding();
scene.SelectedItem = meshWrapper;
}
Rebuilding = false;
Rebuild(null);
}
public void WrapAndAddAsChildren(List<IObject3D> children)
@ -154,7 +160,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
public void ResetMeshWrappers()
public void ResetMeshWrappers(Object3DPropertyFlags flags)
{
// if there are not already, wrap all meshes with our id (some inner object may have changed it's meshes)
AddMeshWrapperToAllChildren();
@ -167,7 +173,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
// set the mesh back to the child mesh
item.Mesh = firstChild.Mesh;
// and reset the properties
firstChild.CopyProperties(firstChild);
var itemMatrix = item.Matrix;
item.CopyProperties(firstChild, flags);
item.Matrix = itemMatrix;
}
}
}

View file

@ -114,7 +114,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
updateButton.Click += (s, e) =>
{
// make sure the mesh on the group is not visible
group.ResetMeshWrappers();
//group.ResetMeshWrappers();
updateButton.Enabled = false;
ProcessBooleans(group);
};
@ -152,7 +152,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
checkBox.CheckedStateChanged += (s, e) =>
{
// make sure the mesh on the group is not visible
group.ResetMeshWrappers();
//group.ResetMeshWrappers();
// and set the output type for this checkbox
item.OutputType = checkBox.Checked ? PrintOutputTypes.Hole : PrintOutputTypes.Solid;

View file

@ -36,25 +36,13 @@ using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.PolygonMesh;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class SubtractObject3D : MeshWrapperObject3D
{
public SubtractObject3D()
{
Name = "Subtract";
}
public override void Rebuild(UndoBuffer undoBuffer)
{
}
}
public class SubtractEditor : IObject3DEditor
{
private SubtractObject3D group;
private SubtractObject3D subtractObject3D;
private View3DWidget view3DWidget;
public string Name => "Subtract";
@ -63,7 +51,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
public GuiWidget Create(IObject3D group, View3DWidget view3DWidget, ThemeConfig theme)
{
this.view3DWidget = view3DWidget;
this.group = group as SubtractObject3D;
this.subtractObject3D = group as SubtractObject3D;
var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
@ -82,7 +70,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
private void AddSubtractSelector(View3DWidget view3DWidget, FlowLayoutWidget tabContainer, ThemeConfig theme)
{
var children = group.Children.ToList();
var children = subtractObject3D.Children.ToList();
tabContainer.AddChild(new TextWidget("Set Subtract")
{
@ -91,6 +79,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
AutoExpandBoundsToText = true,
});
Dictionary<IObject3D, ICheckbox> objectChecks = new Dictionary<IObject3D, ICheckbox>();
// create this early so we can use enable disable it on button changed state
var updateButton = theme.ButtonFactory.Generate("Update".Localize());
updateButton.Margin = new BorderDouble(5);
@ -98,9 +87,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
updateButton.Click += (s, e) =>
{
// make sure the mesh on the group is not visible
group.ResetMeshWrappers();
updateButton.Enabled = PrepareForSubtract(objectChecks);
updateButton.Enabled = false;
ProcessBooleans(group);
subtractObject3D.Rebuild(null);
};
List<GuiWidget> radioSiblings = new List<GuiWidget>();
@ -133,28 +122,22 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
rowContainer.AddChild(selectWidget);
ICheckbox checkBox = selectWidget as ICheckbox;
objectChecks.Add(item, checkBox);
checkBox.CheckedStateChanged += (s, e) =>
{
// make sure the mesh on the group is not visible
group.ResetMeshWrappers();
// and set the output type for this checkbox
item.OutputType = checkBox.Checked ? PrintOutputTypes.Hole : PrintOutputTypes.Solid;
int holeCount = children.Where((o) => o.OutputType == PrintOutputTypes.Hole).Count();
int solidCount = children.Where((o) => o.OutputType != PrintOutputTypes.Hole).Count();
updateButton.Enabled = children.Count != holeCount && children.Count != solidCount;
updateButton.Enabled = PrepareForSubtract(objectChecks);
};
tabContainer.AddChild(rowContainer);
}
bool operationApplied = group.Descendants()
.Where((obj) => obj.OwnerID == group.ID)
bool operationApplied = subtractObject3D.Descendants()
.Where((obj) => obj.OwnerID == subtractObject3D.ID)
.Where((objId) => objId.Mesh != objId.Children.First().Mesh).Any();
bool selectionHasBeenMade = group.Descendants()
.Where((obj) => obj.OwnerID == group.ID && obj.OutputType == PrintOutputTypes.Hole)
bool selectionHasBeenMade = subtractObject3D.Descendants()
.Where((obj) => obj.OwnerID == subtractObject3D.ID && obj.OutputType == PrintOutputTypes.Hole)
.Any();
if (!operationApplied && !selectionHasBeenMade)
@ -174,86 +157,23 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
tabContainer.AddChild(updateButton);
}
private void ProcessBooleans(IObject3D group)
private bool PrepareForSubtract(Dictionary<IObject3D, ICheckbox> objectChecks)
{
// spin up a task to remove holes from the objects in the group
ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
subtractObject3D.Rebuilding = true;
// make sure the mesh on the group is not visible
subtractObject3D.ResetMeshWrappers(Object3DPropertyFlags.All);
var removeObjects = group.Children
.Where((i) => i.OutputType == PrintOutputTypes.Hole)
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == group.ID).ToList();
var keepObjects = group.Children
.Where((i) => i.OutputType != PrintOutputTypes.Hole)
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == group.ID).ToList();
Subtract(keepObjects, removeObjects, cancellationToken, reporter);
return Task.CompletedTask;
});
}
public static void Subtract(List<IObject3D> keepObjects, List<IObject3D> removeObjects)
{
Subtract(keepObjects, removeObjects, CancellationToken.None, null);
}
public static void Subtract(List<IObject3D> keepObjects, List<IObject3D> removeObjects, CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
if (removeObjects.Any()
&& keepObjects.Any())
foreach (var keyValue in objectChecks)
{
var totalOperations = removeObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
ProgressStatus progressStatus = new ProgressStatus();
foreach (var remove in removeObjects)
{
foreach (var keep in keepObjects)
{
progressStatus.Status = "Copy Remove";
reporter?.Report(progressStatus);
var transformedRemove = Mesh.Copy(remove.Mesh, cancellationToken);
transformedRemove.Transform(remove.WorldMatrix());
progressStatus.Status = "Copy Keep";
reporter?.Report(progressStatus);
var transformedKeep = Mesh.Copy(keep.Mesh, cancellationToken);
transformedKeep.Transform(keep.WorldMatrix());
progressStatus.Status = "Do CSG";
reporter?.Report(progressStatus);
transformedKeep = PolygonMesh.Csg.CsgOperations.Subtract(transformedKeep, transformedRemove, (status, progress0To1) =>
{
// Abort if flagged
cancellationToken.ThrowIfCancellationRequested();
progressStatus.Status = status;
progressStatus.Progress0To1 = percentCompleted + amountPerOperation * progress0To1;
reporter?.Report(progressStatus);
}, cancellationToken);
var inverse = keep.WorldMatrix();
inverse.Invert();
transformedKeep.Transform(inverse);
keep.Mesh = transformedKeep;
// TODO: make this the subtract object when it is available
keep.Invalidate(new InvalidateArgs(keep, InvalidateType.Content));
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
remove.Visible = false;
}
// and set the output type for this checkbox
keyValue.Key.OutputType = keyValue.Value.Checked ? PrintOutputTypes.Hole : PrintOutputTypes.Solid;
}
int holeCount = objectChecks.Where((o) => o.Key.OutputType == PrintOutputTypes.Hole).Count();
int solidCount = objectChecks.Where((o) => o.Key.OutputType != PrintOutputTypes.Hole).Count();
subtractObject3D.Rebuilding = false;
return objectChecks.Count != holeCount && objectChecks.Count != solidCount;
}
}
}

View file

@ -0,0 +1,154 @@
/*
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;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.PolygonMesh;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class SubtractObject3D : MeshWrapperObject3D
{
public SubtractObject3D()
{
Name = "Subtract";
}
public override void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType == InvalidateType.Content
|| invalidateType.InvalidateType == InvalidateType.Matrix)
&& invalidateType.Source != this
&& !Rebuilding)
{
Rebuild(null);
}
base.OnInvalidate(invalidateType);
}
public override void Rebuild(UndoBuffer undoBuffer)
{
Rebuilding = true;
ResetMeshWrappers(Object3DPropertyFlags.All & (~Object3DPropertyFlags.OutputType));
// spin up a task to remove holes from the objects in the group
ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
var removeObjects = this.Children
.Where((i) => i.WorldOutputType(this) == PrintOutputTypes.Hole)
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where((i) => i.WorldOutputType(this) != PrintOutputTypes.Hole)
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
Subtract(keepObjects, removeObjects, cancellationToken, reporter);
Rebuilding = false;
UiThread.RunOnIdle(() => base.Rebuild(undoBuffer));
return Task.CompletedTask;
});
base.Rebuild(null);
}
public static void Subtract(List<IObject3D> keepObjects, List<IObject3D> removeObjects)
{
Subtract(keepObjects, removeObjects, CancellationToken.None, null);
}
public static void Subtract(List<IObject3D> keepObjects, List<IObject3D> removeObjects, CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
if (removeObjects.Any()
&& keepObjects.Any())
{
var totalOperations = removeObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
ProgressStatus progressStatus = new ProgressStatus();
foreach (var remove in removeObjects)
{
foreach (var keep in keepObjects)
{
progressStatus.Status = "Copy Remove";
reporter?.Report(progressStatus);
var transformedRemove = Mesh.Copy(remove.Mesh, cancellationToken);
transformedRemove.Transform(remove.WorldMatrix());
progressStatus.Status = "Copy Keep";
reporter?.Report(progressStatus);
var transformedKeep = Mesh.Copy(keep.Mesh, cancellationToken);
transformedKeep.Transform(keep.WorldMatrix());
progressStatus.Status = "Do CSG";
reporter?.Report(progressStatus);
transformedKeep = PolygonMesh.Csg.CsgOperations.Subtract(transformedKeep, transformedRemove, (status, progress0To1) =>
{
// Abort if flagged
cancellationToken.ThrowIfCancellationRequested();
progressStatus.Status = status;
progressStatus.Progress0To1 = percentCompleted + amountPerOperation * progress0To1;
reporter?.Report(progressStatus);
}, cancellationToken);
var inverse = keep.WorldMatrix();
inverse.Invert();
transformedKeep.Transform(inverse);
keep.Mesh = transformedKeep;
// TODO: make this the subtract object when it is available
keep.Invalidate(new InvalidateArgs(keep, InvalidateType.Content));
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
remove.Visible = false;
}
}
}
}
}

View file

@ -65,6 +65,32 @@ namespace MatterHackers.MeshVisualizer
public static class MaterialRendering
{
public static void RenderCylinderOutline(this WorldView world, Matrix4X4 worldMatrix, Vector3 center, double Diameter, double Height, int sides, Color color, double lineWidth = 1, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
for (int i = 0; i < sides; i++)
{
var rotatedPoint = new Vector3(Math.Cos(MathHelper.Tau * i / sides), Math.Sin(MathHelper.Tau * i / sides), 0) * Diameter / 2;
var sideTop = Vector3.Transform(center + rotatedPoint + new Vector3(0, 0, Height / 2), worldMatrix);
var sideBottom = Vector3.Transform(center + rotatedPoint + new Vector3(0, 0, -Height / 2), worldMatrix);
var rotated2Point = new Vector3(Math.Cos(MathHelper.Tau * (i + 1) / sides), Math.Sin(MathHelper.Tau * (i + 1) / sides), 0) * Diameter / 2;
var topStart = sideTop;
var topEnd = Vector3.Transform(center + rotated2Point + new Vector3(0, 0, Height / 2), worldMatrix);
var bottomStart = sideBottom;
var bottomEnd = Vector3.Transform(center + rotated2Point + new Vector3(0, 0, -Height / 2), worldMatrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideTop, ref sideBottom, extendLineLength);
}
world.Render3DLineNoPrep(frustum, sideTop, sideBottom, color, lineWidth);
world.Render3DLineNoPrep(frustum, topStart, topEnd, color, lineWidth);
world.Render3DLineNoPrep(frustum, bottomStart, bottomEnd, color, lineWidth);
}
}
public static void RenderAabb(this WorldView world, AxisAlignedBoundingBox bounds, Matrix4X4 matrix, Color color, double width, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);

@ -1 +1 @@
Subproject commit 876ff2b4051b30fb983faecedfe0d81e19f3dac3
Subproject commit 13913b605d420e97a5fb22d102e4ecc7a26cdfc0