diff --git a/MatterControlLib/ApplicationView/Application.cs b/MatterControlLib/ApplicationView/Application.cs
index 0dd637eef..3cd7efceb 100644
--- a/MatterControlLib/ApplicationView/Application.cs
+++ b/MatterControlLib/ApplicationView/Application.cs
@@ -276,12 +276,18 @@ namespace MatterHackers.MatterControl
|| (view3D.Printer != null && view3D.Printer.ViewState.ViewMode == PartViewMode.Model))
{
var scene = view3D.sceneContext.Scene;
- if (scene.SelectedItem != null
- && scene.SelectedItem is SelectionGroupObject3D
- && scene.SelectedItem.Children.Count > 1)
+ if (scene.SelectedItem != null)
{
- var group = new GroupHolesAppliedObject3D();
- group.WrapSelectedItemAndSelect(scene);
+ if (keyEvent.Shift)
+ {
+ scene.UngroupSelection();
+ }
+ else if (scene.SelectedItem is SelectionGroupObject3D
+ && scene.SelectedItem.Children.Count > 1)
+ {
+ var group = new GroupHolesAppliedObject3D();
+ group.WrapSelectedItemAndSelect(scene);
+ }
}
}
diff --git a/MatterControlLib/ApplicationView/SceneOperations.cs b/MatterControlLib/ApplicationView/SceneOperations.cs
index 84ef96751..e97516c40 100644
--- a/MatterControlLib/ApplicationView/SceneOperations.cs
+++ b/MatterControlLib/ApplicationView/SceneOperations.cs
@@ -1519,6 +1519,7 @@ namespace MatterHackers.MatterControl
return false;
},
Icon = (theme) => StaticData.Instance.LoadIcon("ungroup.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(),
+ UiHint = "Shift + G".Localize(),
};
}
}
diff --git a/MatterControlLib/CustomWidgets/SceneOperation.cs b/MatterControlLib/CustomWidgets/SceneOperation.cs
index 82d3cc22f..3d2175d40 100644
--- a/MatterControlLib/CustomWidgets/SceneOperation.cs
+++ b/MatterControlLib/CustomWidgets/SceneOperation.cs
@@ -99,9 +99,9 @@ namespace MatterHackers.Agg.UI
public EventHandler CollapseChanged;
- private static string CollapseKey(string opperationGroupName)
+ private static string CollapseKey(string operationGroupName)
{
- return $"scene_operation_collapse_{opperationGroupName}";
+ return $"scene_operation_collapse_{operationGroupName}";
}
public bool Collapse
@@ -123,9 +123,9 @@ namespace MatterHackers.Agg.UI
public EventHandler VisibleChanged;
- private static string VisibleKey(string opperationGroupName)
+ private static string VisibleKey(string operationGroupName)
{
- return $"scene_operation_visible_{opperationGroupName}";
+ return $"scene_operation_visible_{operationGroupName}";
}
public static bool GetVisible(string id, bool defaultIfNotSet)
diff --git a/MatterControlLib/DesignTools/Operations/AlignObject3D_3.cs b/MatterControlLib/DesignTools/Operations/AlignObject3D_3.cs
new file mode 100644
index 000000000..fa2c00b95
--- /dev/null
+++ b/MatterControlLib/DesignTools/Operations/AlignObject3D_3.cs
@@ -0,0 +1,434 @@
+/*
+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 System.ComponentModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MatterHackers.Agg.UI;
+using MatterHackers.DataConverters3D;
+using MatterHackers.Localizations;
+using MatterHackers.MatterControl.PartPreviewWindow.View3D;
+using MatterHackers.VectorMath;
+using Newtonsoft.Json;
+using Aabb = MatterHackers.VectorMath.AxisAlignedBoundingBox;
+
+namespace MatterHackers.MatterControl.DesignTools.Operations
+{
+ public class AlignObject3D_3 : OperationSourceContainerObject3D, IPropertyGridModifier, IBuildsOnThread
+ {
+ private CancellationTokenSource cancellationToken;
+
+ public AlignObject3D_3()
+ {
+ Name = "Align";
+ }
+
+ [ShowAsList]
+ [DisplayName("Anchor")]
+ public SelectedChildren SelectedChild { get; set; } = new SelectedChildren();
+
+ [SectionStart("X Axis"), DisplayName("Align")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_left.png", "align_center_x.png", "align_right.png", "align_origin.png" }, InvertIcons = true)]
+ public Align XAlign { get; set; } = Align.None;
+
+ public bool XOptions { get; set; } = false;
+
+ [DisplayName("SubAlign")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_to_right.png", "align_to_center_x.png", "align_to_left.png", "align_origin.png" }, InvertIcons = true)]
+ public Align XSubAlign { get; set; } = Align.None;
+
+ [DisplayName("Offset")]
+ [Slider(-20, 20, useSnappingGrid: true)]
+ public DoubleOrExpression XOffset { get; set; } = 0;
+
+ [SectionStart("Y Axis"), DisplayName("Align")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_bottom.png", "align_center_y.png", "align_top.png", "align_origin.png" }, InvertIcons = true)]
+ public Align YAlign { get; set; } = Align.None;
+
+ public bool YOptions { get; set; } = false;
+
+ [DisplayName("SubAlign")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_to_top.png", "align_to_center_y.png", "align_to_bottom.png", "align_origin.png" }, InvertIcons = true)]
+ public Align YSubAlign { get; set; } = Align.None;
+
+ [DisplayName("Offset")]
+ [Slider(-20, 20, useSnappingGrid: true)]
+ public DoubleOrExpression YOffset { get; set; } = 0;
+
+ [SectionStart("Z Axis"), DisplayName("Align")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_bottom.png", "align_center_y.png", "align_top.png", "align_origin.png" }, InvertIcons = true)]
+ public Align ZAlign { get; set; } = Align.None;
+
+ public bool ZOptions { get; set; } = false;
+
+ [DisplayName("SubAlign")]
+ [EnumDisplay(IconPaths = new string[] { "424.png", "align_to_top.png", "align_to_center_y.png", "align_to_bottom.png", "align_origin.png" }, InvertIcons = true)]
+ public Align ZSubAlign { get; set; } = Align.None;
+
+ [DisplayName("Offset")]
+ [Slider(-20, 20, useSnappingGrid: true)]
+ public DoubleOrExpression ZOffset { get; set; } = 0;
+
+ public override bool CanApply => true;
+
+ [JsonIgnore]
+ private Aabb AnchorBounds
+ {
+ get
+ {
+ var aabb = this.GetAxisAlignedBoundingBox();
+
+ if (SelectedChild.Count > 0)
+ {
+ if (Children.Where(c => SelectedChild.FirstOrDefault() == c.ID).FirstOrDefault() == null)
+ {
+ // none of our children have the selected id so clear the list
+ SelectedChild.Clear();
+ }
+ }
+
+ if (SelectedChild.Count == 0)
+ {
+ SelectedChild.Add(this.Children.FirstOrDefault().ID);
+ }
+
+ var sourceChild = this.Children.Where(c => c.ID == SelectedChild.FirstOrDefault()).FirstOrDefault();
+
+ if (sourceChild != null)
+ {
+ aabb = sourceChild.GetAxisAlignedBoundingBox();
+ }
+
+ return aabb;
+ }
+ }
+
+ [JsonIgnore]
+ private IObject3D SelectedObject
+ {
+ get
+ {
+ if (SelectedChild.Count > 0)
+ {
+ return this.Children.Where(c => c.ID == SelectedChild.FirstOrDefault()).FirstOrDefault();
+ }
+
+ return this.Children.FirstOrDefault();
+ }
+ }
+
+ public static Vector3 GetPositionToAlignTo(IObject3D objectToAlignTo, FaceAlign boundingFacesToAlignTo, Vector3 extraOffset)
+ {
+ var positionToAlignTo = default(Vector3);
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Left, FaceAlign.Right))
+ {
+ positionToAlignTo.X = objectToAlignTo.GetAxisAlignedBoundingBox().MinXYZ.X;
+ }
+
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Right, FaceAlign.Left))
+ {
+ positionToAlignTo.X = objectToAlignTo.GetAxisAlignedBoundingBox().MaxXYZ.X;
+ }
+
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Front, FaceAlign.Back))
+ {
+ positionToAlignTo.Y = objectToAlignTo.GetAxisAlignedBoundingBox().MinXYZ.Y;
+ }
+
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Back, FaceAlign.Front))
+ {
+ positionToAlignTo.Y = objectToAlignTo.GetAxisAlignedBoundingBox().MaxXYZ.Y;
+ }
+
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Bottom, FaceAlign.Top))
+ {
+ positionToAlignTo.Z = objectToAlignTo.GetAxisAlignedBoundingBox().MinXYZ.Z;
+ }
+
+ if (IsSet(boundingFacesToAlignTo, FaceAlign.Top, FaceAlign.Bottom))
+ {
+ positionToAlignTo.Z = objectToAlignTo.GetAxisAlignedBoundingBox().MaxXYZ.Z;
+ }
+
+ return positionToAlignTo + extraOffset;
+ }
+
+ 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 (invalidateArgs.InvalidateType.HasFlag(InvalidateType.Name)
+ && !NameOverriden)
+ {
+ Name = NameFromChildren();
+ NameOverriden = false;
+ base.OnInvalidate(invalidateArgs);
+ }
+ else if (SheetObject3D.NeedsRebuild(this, invalidateArgs))
+ {
+ await Rebuild();
+ }
+ else
+ {
+ // and also always pass back the actual type
+ base.OnInvalidate(invalidateArgs);
+ }
+ }
+
+ 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");
+
+ return ApplicationController.Instance.Tasks.Execute(
+ "Combine".Localize(),
+ null,
+ (reporter, cancellationTokenSource) =>
+ {
+ this.cancellationToken = cancellationTokenSource;
+
+ using (RebuildLock())
+ {
+ RemoveAllButSource();
+
+ // reset the position
+ Children.Modify(list =>
+ {
+ foreach (var child in SourceContainer.Children)
+ {
+ list.Add(child.Clone());
+ }
+ });
+
+ SourceContainer.Visible = false;
+
+ this.Children.Modify(list =>
+ {
+ if (list.Count == 0)
+ {
+ return;
+ }
+
+ var anchorBounds = AnchorBounds;
+ var children = list.Where(c => c.GetType() != typeof(OperationSourceObject3D)
+ && c.ID != SelectedChild.FirstOrDefault());
+
+ Align MapSubAlign(Align align)
+ {
+ switch (align)
+ {
+ case Align.Min:
+ return Align.Max;
+ case Align.Max:
+ return Align.Min;
+ default:
+ return align;
+ }
+ }
+
+ // align all the objects to the anchor
+ foreach (var child in children)
+ {
+ if (XAlign != Align.None)
+ {
+ AlignAxis(0,
+ (XOptions && XSubAlign != Align.None) ? MapSubAlign(XSubAlign) : XAlign,
+ GetSubAlignOffset(anchorBounds, 0, XAlign),
+ XOffset.Value(this),
+ child);
+ }
+ if (YAlign != Align.None)
+ {
+ AlignAxis(1,
+ (YOptions && YSubAlign != Align.None) ? MapSubAlign(YSubAlign) : YAlign,
+ GetSubAlignOffset(anchorBounds, 1, YAlign),
+ YOffset.Value(this),
+ child);
+ }
+ if (ZAlign != Align.None)
+ {
+ AlignAxis(2,
+ (ZOptions && ZSubAlign != Align.None) ? MapSubAlign(ZSubAlign) : ZAlign,
+ GetSubAlignOffset(anchorBounds, 2, ZAlign),
+ ZOffset.Value(this),
+ child);
+ }
+ }
+ });
+
+ if (!NameOverriden)
+ {
+ Name = NameFromChildren();
+ NameOverriden = false;
+ }
+
+ var removeItems = Children.Where(c => c.OutputType == PrintOutputTypes.Hole && c.Visible);
+ if (removeItems.Any())
+ {
+ var keepItems = Children.Where(c => c.OutputType != PrintOutputTypes.Hole && c.Visible);
+
+ // apply any holes before we return
+ var resultItems = SubtractObject3D_2.DoSubtract(this,
+ keepItems,
+ removeItems,
+ reporter,
+ cancellationToken.Token);
+
+ RemoveAllButSource();
+
+ // add back in the results of the hole removal
+ Children.Modify(list =>
+ {
+ foreach (var child in resultItems)
+ {
+ list.Add(child);
+ child.Visible = true;
+ }
+ });
+ }
+ }
+
+ this.cancellationToken = null;
+ UiThread.RunOnIdle(() =>
+ {
+ this.CancelAllParentBuilding();
+ Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Matrix));
+ });
+
+ return Task.CompletedTask;
+ });
+ }
+
+ public void UpdateControls(PublicPropertyChange change)
+ {
+ change.SetRowVisible(nameof(XSubAlign), () => XOptions);
+ change.SetRowVisible(nameof(XOffset), () => XOptions);
+ change.SetRowVisible(nameof(YSubAlign), () => YOptions);
+ change.SetRowVisible(nameof(YOffset), () => YOptions);
+ change.SetRowVisible(nameof(ZSubAlign), () => ZOptions);
+ change.SetRowVisible(nameof(ZOffset), () => ZOptions);
+ }
+
+ private static bool IsSet(FaceAlign variableToCheck, FaceAlign faceToCheckFor, FaceAlign faceToAssertNot)
+ {
+ if ((variableToCheck & faceToCheckFor) != 0)
+ {
+ if ((variableToCheck & faceToAssertNot) != 0)
+ {
+ throw new Exception("You cannot have both " + faceToCheckFor.ToString() + " and " + faceToAssertNot.ToString() + " set when calling Align. The are mutually exclusive.");
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void AlignAxis(int axis, Align align, double alignTo, double offset, IObject3D item)
+ {
+ var aabb = item.GetAxisAlignedBoundingBox();
+ var translate = Vector3.Zero;
+
+ switch (align)
+ {
+ case Align.Min:
+ translate[axis] = alignTo - aabb.MinXYZ[axis] + offset;
+ break;
+
+ case Align.Center:
+ translate[axis] = alignTo - aabb.Center[axis] + offset;
+ break;
+
+ case Align.Max:
+ translate[axis] = alignTo - aabb.MaxXYZ[axis] + offset;
+ break;
+
+ case Align.Origin:
+ // find the origin in world space of the item
+ var itemOrigin = Vector3Ex.Transform(Vector3.Zero, item.WorldMatrix(this));
+ translate[axis] = alignTo - itemOrigin[axis] + offset;
+ break;
+ }
+
+ item.Translate(translate);
+ }
+
+ private double GetSubAlignOffset(Aabb anchorBounds, int axis, Align alignTo)
+ {
+ switch (alignTo)
+ {
+ case Align.None:
+ return 0;
+
+ case Align.Min:
+ return anchorBounds.MinXYZ[axis];
+
+ case Align.Center:
+ return anchorBounds.Center[axis];
+
+ case Align.Max:
+ return anchorBounds.MaxXYZ[axis];
+
+ case Align.Origin:
+ return Vector3Ex.Transform(Vector3.Zero, SelectedObject.WorldMatrix(this))[axis];
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ public string NameFromChildren()
+ {
+ return CalculateName(this.Children, ", ");
+ }
+ }
+}
\ No newline at end of file
diff --git a/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs b/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs
index 6d3306109..fed67d14f 100644
--- a/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs
+++ b/MatterControlLib/DesignTools/Operations/CurveObject3D_3.cs
@@ -31,6 +31,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
@@ -39,6 +40,7 @@ using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
+using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using MatterHackers.PolygonMesh;
using MatterHackers.RenderOpenGl;
using MatterHackers.RenderOpenGl.OpenGl;
@@ -67,6 +69,9 @@ namespace MatterHackers.MatterControl.DesignTools
Diameter,
}
+ [HideFromEditor]
+ public Vector3 PostCurveOffset { get; set; } = new Vector3();
+
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Tabs)]
public BendTypes BendType { get; set; } = BendTypes.Angle;
@@ -205,7 +210,13 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
- public override Task Rebuild()
+ public override void Cancel(UndoBuffer undoBuffer)
+ {
+ this.Matrix *= Matrix4X4.CreateTranslation(-PostCurveOffset);
+ base.Cancel(undoBuffer);
+ }
+
+ public override Task Rebuild()
{
this.DebugDepth("Rebuild");
@@ -346,11 +357,14 @@ namespace MatterHackers.MatterControl.DesignTools
if (firstBuild)
{
var postAabb = this.GetAxisAlignedBoundingBox();
- this.Matrix *= Matrix4X4.CreateTranslation(initialAabb.Center.X - postAabb.Center.X,
+ PostCurveOffset = new Vector3(initialAabb.Center.X - postAabb.Center.X,
initialAabb.MinXYZ.Y - postAabb.MinXYZ.Y,
initialAabb.MinXYZ.Z - postAabb.MinXYZ.Z);
+ this.Matrix *= Matrix4X4.CreateTranslation(PostCurveOffset);
}
+ ApplyHoles(reporter, cancellationToken.Token);
+
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
diff --git a/MatterControlLib/DesignTools/Operations/GroupObject3D.cs b/MatterControlLib/DesignTools/Operations/GroupObject3D.cs
index cee1e16b1..1682aafc2 100644
--- a/MatterControlLib/DesignTools/Operations/GroupObject3D.cs
+++ b/MatterControlLib/DesignTools/Operations/GroupObject3D.cs
@@ -55,6 +55,8 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
Name = "Group".Localize();
}
+ // We can't use Subtracts Apply as it will leave a group if there are multiple object results
+ // and we want to always leave the individual results after the ungroup.
public override void Apply(UndoBuffer undoBuffer)
{
using (RebuildLock())
@@ -91,7 +93,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
var selections = new SelectedChildren();
- foreach(var child in SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf().Children.Where(i => i.WorldOutputType(this) == PrintOutputTypes.Hole && !(i is OperationSourceObject3D) ))
+ foreach(var child in SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Where(i => i.WorldOutputType(this) == PrintOutputTypes.Hole && !(i is OperationSourceObject3D) ))
{
selections.Add(child.ID);
}
diff --git a/MatterControlLib/DesignTools/Operations/PinchObject3D_3.cs b/MatterControlLib/DesignTools/Operations/PinchObject3D_3.cs
index 3db90225a..f52428dd9 100644
--- a/MatterControlLib/DesignTools/Operations/PinchObject3D_3.cs
+++ b/MatterControlLib/DesignTools/Operations/PinchObject3D_3.cs
@@ -115,6 +115,7 @@ namespace MatterHackers.MatterControl.DesignTools
// set the matrix back
Matrix = currentMatrix;
SourceContainer.Visible = false;
+ ApplyHoles(reporter, cancellationToken.Token);
rebuildLocks.Dispose();
Invalidate(InvalidateType.DisplayValues);
diff --git a/MatterControlLib/DesignTools/Operations/TwistObject3D.cs b/MatterControlLib/DesignTools/Operations/TwistObject3D.cs
index 598d7978d..f518af265 100644
--- a/MatterControlLib/DesignTools/Operations/TwistObject3D.cs
+++ b/MatterControlLib/DesignTools/Operations/TwistObject3D.cs
@@ -331,6 +331,8 @@ namespace MatterHackers.MatterControl.DesignTools
list.AddRange(twistedChildren);
});
+ ApplyHoles(reporter, cancellationToken.Token);
+
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
diff --git a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs
index a17ac147a..5ddd7d5e9 100644
--- a/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs
+++ b/MatterControlLib/DesignTools/Primitives/OperationSourceContainerObject3D.cs
@@ -37,6 +37,7 @@ using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.DataConverters3D.UndoCommands;
using MatterHackers.Localizations;
+using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using MatterHackers.PolygonMesh;
using Newtonsoft.Json;
@@ -320,6 +321,49 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
this.Invalidate(InvalidateType.Children);
}
+
+ ///
+ /// Use after source itmes have been processed to modify the transformed children of the inherited operation.
+ /// Example: A bend operation has been applied and there are still holes, this will remove the holes.
+ ///
+ /// Show progress
+ /// Can check if the operation has been canceled
+ /// Did any holes get subtracted
+ public bool ApplyHoles(IProgress reporter,
+ CancellationToken cancellationToken)
+ {
+ var removeItems = Children.Where(c => c.OutputType == PrintOutputTypes.Hole && c.Visible);
+ if (removeItems.Any())
+ {
+ var keepItems = Children.Where(c => c.OutputType != PrintOutputTypes.Hole && c.Visible);
+
+ if (keepItems.Any())
+ {
+ // apply any holes before we return
+ var resultItems = SubtractObject3D_2.DoSubtract(null,
+ keepItems,
+ removeItems,
+ reporter,
+ cancellationToken);
+
+ RemoveAllButSource();
+
+ // add back in the results of the hole removal
+ Children.Modify(list =>
+ {
+ foreach (var child in resultItems)
+ {
+ list.Add(child);
+ child.Visible = true;
+ }
+ });
+
+ return true;
+ }
+ }
+
+ return false;
+ }
}
public class OperationSourceObject3D : Object3D
diff --git a/MatterControlLib/DesignTools/PublicPropertyEditor.cs b/MatterControlLib/DesignTools/PublicPropertyEditor.cs
index 96755e9e9..5c4fd3e84 100644
--- a/MatterControlLib/DesignTools/PublicPropertyEditor.cs
+++ b/MatterControlLib/DesignTools/PublicPropertyEditor.cs
@@ -422,7 +422,7 @@ namespace MatterHackers.MatterControl.DesignTools
}
else if (propertyValue is Color color)
{
- var field = new ColorField(theme, object3D.Color, null);
+ var field = new ColorField(theme, object3D.Color, null, false);
field.Initialize(0);
field.ValueChanged += (s, e) =>
{
@@ -1281,7 +1281,7 @@ namespace MatterHackers.MatterControl.DesignTools
Margin = new BorderDouble(0, 3, 0, 0),
};
- var parentOfSubtractTargets = sourceContainer.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ var parentOfSubtractTargets = sourceContainer.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var sourceChildren = parentOfSubtractTargets.Children.ToList();
diff --git a/MatterControlLib/PartPreviewWindow/ItemColorButton.cs b/MatterControlLib/PartPreviewWindow/ItemColorButton.cs
index ec405cab9..f71dea13c 100644
--- a/MatterControlLib/PartPreviewWindow/ItemColorButton.cs
+++ b/MatterControlLib/PartPreviewWindow/ItemColorButton.cs
@@ -33,7 +33,6 @@ using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
-using MatterHackers.DataConverters3D;
using MatterHackers.ImageProcessing;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
@@ -51,7 +50,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
public bool IsOpen => popupContent?.ContainsFocus == true;
- public ItemColorButton(ThemeConfig theme, Color selectedColor, Action> getPickedColor)
+ private static int TransparentAmount => 120;
+
+ public ItemColorButton(ThemeConfig theme, Color selectedColor, Action> getPickedColor, bool transparentCheckbox)
{
this.ToolTipText = "Color".Localize();
var scaledButtonSize = 24 * GuiWidget.DeviceScale;
@@ -82,7 +83,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
this.DynamicPopupContent = () =>
{
- popupContent = NewColorSelector(theme, colorButton.BackgroundColor, menuTheme, (color) => colorButton.BackgroundColor = color, getPickedColor);
+ popupContent = NewColorSelector(theme, colorButton.BackgroundColor, menuTheme, (color) => colorButton.BackgroundColor = color, getPickedColor, transparentCheckbox);
return popupContent;
};
@@ -100,11 +101,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
public static GuiWidget NewColorSelector(ThemeConfig theme,
- Color selectedColor,
+ Color startingColor,
ThemeConfig menuTheme,
Action update,
- Action> getPickedColor)
+ Action> getPickedColor,
+ bool transparentCheckbox)
{
+ CheckBox transparent = null;
+
var content = new FlowLayoutWidget(FlowDirection.LeftToRight)
{
Padding = new BorderDouble(5),
@@ -116,7 +120,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var pickerContainer = content.AddChild(new GuiWidget(128 * DeviceScale, 128 * DeviceScale));
var picker = pickerContainer.AddChild(new RadialColorPicker()
{
- SelectedColor = selectedColor.WithAlpha(255),
+ SelectedColor = startingColor.WithAlpha(255),
BackgroundColor = Color.Transparent,
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch,
@@ -124,6 +128,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
picker.SelectedColorChanged += (s, newColor) =>
{
htmlField.SetValue(picker.SelectedColor.Html.Substring(1, 6), false);
+ if (transparent?.Checked == true)
+ {
+ picker.SelectedColor = picker.SelectedColor.WithAlpha(TransparentAmount);
+ }
+ else
+ {
+ picker.SelectedColor = picker.SelectedColor.WithAlpha(255);
+ }
update?.Invoke(picker.SelectedColor);
};
@@ -135,7 +147,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// put in an html edit field
htmlField.Initialize(0);
- htmlField.SetValue(selectedColor.Html.Substring(1, 6), false);
+ htmlField.SetValue(startingColor.Html.Substring(1, 6), false);
htmlField.ClearUndoHistory();
htmlField.ValueChanged += (s, e) =>
{
@@ -150,11 +162,18 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
// we did not understand the color
// set it back to selectedColor
- htmlField.SetValue(selectedColor.Html.Substring(1, 6), false);
+ htmlField.SetValue(startingColor.Html.Substring(1, 6), false);
}
else // valid color set
{
- picker.SelectedColor = color;
+ if (transparent?.Checked == true)
+ {
+ picker.SelectedColor = color.WithAlpha(TransparentAmount);
+ }
+ else
+ {
+ picker.SelectedColor = color.WithAlpha(255);
+ }
}
};
@@ -193,6 +212,35 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
picker.SelectedColorChanged += (s, newColor) => colorSwatch.BackgroundColor = picker.SelectedColor;
+ if (transparentCheckbox)
+ {
+ transparent = new CheckBox("Transparent".Localize())
+ {
+ TextColor = theme.TextColor,
+ Margin = new BorderDouble(15, 0, 0, 3),
+ HAnchor = HAnchor.Fit | HAnchor.Left,
+ VAnchor = VAnchor.Absolute,
+ ToolTipText = "Set the rendering for the object to be transparent".Localize(),
+ Checked = startingColor.Alpha0To255 < 255,
+ };
+
+ rightContent.AddChild(transparent);
+
+ transparent.CheckedStateChanged += (s, e) =>
+ {
+ if (transparent.Checked)
+ {
+ picker.SelectedColor = picker.SelectedColor.WithAlpha(TransparentAmount);
+ }
+ else
+ {
+ picker.SelectedColor = picker.SelectedColor.WithAlpha(255);
+ }
+
+ update?.Invoke(picker.SelectedColor);
+ };
+ }
+
var resetButton = rightContent.AddChild(new TextIconButton("Clear".Localize(), StaticData.Instance.LoadIcon("transparent_grid.png", 16, 16), theme)
{
Margin = new BorderDouble(0, 0, 0, 3),
@@ -222,7 +270,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
getPickedColor((color) =>
{
update?.Invoke(color);
- picker.SelectedColor = color;
+ if (transparent?.Checked == true)
+ {
+ picker.SelectedColor = color.WithAlpha(TransparentAmount);
+ }
+ else
+ {
+ picker.SelectedColor = color;
+ }
});
};
diff --git a/MatterControlLib/PartPreviewWindow/MaterialControls.cs b/MatterControlLib/PartPreviewWindow/MaterialControls.cs
index a6ebb3d27..038b3a5e3 100644
--- a/MatterControlLib/PartPreviewWindow/MaterialControls.cs
+++ b/MatterControlLib/PartPreviewWindow/MaterialControls.cs
@@ -69,7 +69,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var scaledButtonSize = 24 * GuiWidget.DeviceScale;
GuiWidget colorButton;
- buttonView.AddChild(colorButton = new ItemColorButton(theme, MaterialRendering.Color(printer, extruderIndex, theme.BorderColor), null)
+ buttonView.AddChild(colorButton = new ItemColorButton(theme, MaterialRendering.Color(printer, extruderIndex, theme.BorderColor), null, false)
{
Width = scaledButtonSize,
Height = scaledButtonSize,
diff --git a/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs b/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs
index 04ebea4ef..a3d87b28d 100644
--- a/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs
+++ b/MatterControlLib/PartPreviewWindow/SelectedObjectPanel.cs
@@ -255,7 +255,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var selection = scene.SelectedItem;
if (selection != null)
{
- setColor?.Invoke(selection.Color);
+ setColor?.Invoke(selection.WorldColor());
scene.SelectionChanged -= SelectionChanged;
cancellationToken?.Cancel();
scene.SelectedItem = startingSelection;
@@ -288,8 +288,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
if (!(selectedItem.GetType().GetCustomAttributes(typeof(HideMeterialAndColor), true).FirstOrDefault() is HideMeterialAndColor))
{
+ var firstDetectedColor = selectedItem.VisibleMeshes()?.FirstOrDefault()?.WorldColor();
+ var worldColor = Color.White;
+ if (firstDetectedColor != null)
+ {
+ worldColor = firstDetectedColor.Value;
+ }
+
// put in a color edit field
- var colorField = new ColorField(theme, selectedItem.Color, GetNextSelectionColor);
+ var colorField = new ColorField(theme, worldColor, GetNextSelectionColor, true);
colorField.Initialize(0);
colorField.ValueChanged += (s, e) =>
{
@@ -299,7 +306,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
};
- colorField.Content.MouseDown += (s, e) =>
+ var colorButton = colorField.Content.Descendants().FirstOrDefault();
+ colorButton.Parent.MouseDown += (s, e) =>
{
// make sure the render mode is set to shaded or outline
if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Shaded
@@ -308,32 +316,57 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// make sure the render mode is set to outline
sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines;
}
+
+ var currentOutputType = selectedItem.WorldOutputType();
+ if (currentOutputType != PrintOutputTypes.Solid && currentOutputType != PrintOutputTypes.Default)
+ {
+ undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color));
+ }
};
- editorPanel.AddChild(new SettingsRow("Color".Localize(), null, colorField.Content, theme)
+ var colorRow = new SettingsRow("Result".Localize(), null, colorField.Content, theme)
{
// Special top border style for first item in editor
Border = new BorderDouble(0, 1)
- });
+ };
+ editorPanel.AddChild(colorRow);
- // put in a hole edit field
- var holeField = new ToggleboxField(theme);
- holeField.Initialize(0);
- holeField.Checked = selectedItem.WorldOutputType() == PrintOutputTypes.Hole;
- holeField.ValueChanged += (s, e) =>
+ // put in a hole button
+ var scaledButtonSize = 24 * GuiWidget.DeviceScale;
+ var holeButton = new ColorButton(Color.DarkGray)
{
- if (selectedItem.OutputType == PrintOutputTypes.Hole)
- {
- undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color));
- }
- else
+ Margin = new BorderDouble(5, 0, 11, 0),
+ Width = scaledButtonSize,
+ Height = scaledButtonSize,
+ BackgroundRadius = scaledButtonSize / 2,
+ BackgroundOutlineWidth = 1,
+ VAnchor = VAnchor.Center,
+ DisabledColor = theme.MinimalShade,
+ BorderColor = theme.TextColor,
+ ToolTipText = "Convert to Hole".Localize(),
+ };
+
+ var buttonRow = colorButton.Parents().FirstOrDefault();
+ buttonRow.AddChild(new TextWidget("Solid".Localize(), pointSize: theme.FontSize10, textColor: theme.TextColor)
+ {
+ VAnchor = VAnchor.Center,
+ Margin = new BorderDouble(3, 0),
+ }, 0);
+ buttonRow.AddChild(holeButton, 0);
+ buttonRow.AddChild(new TextWidget("Hole".Localize(), pointSize: theme.FontSize10, textColor: theme.TextColor)
+ {
+ VAnchor = VAnchor.Center,
+ Margin = new BorderDouble(3, 0),
+ }, 0);
+
+ holeButton.Click += (s, e) =>
+ {
+ if (selectedItem.WorldOutputType() != PrintOutputTypes.Hole)
{
undoBuffer.AddAndDo(new MakeHole(selectedItem));
}
};
- editorPanel.AddChild(new SettingsRow("Hole".Localize(), null, holeField.Content, theme));
-
// put in a material edit field
var materialField = new MaterialIndexField(sceneContext.Printer, theme, selectedItem.MaterialIndex);
materialField.Initialize(0);
diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs
index e10af18a0..4434acc35 100644
--- a/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs
+++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/CombineObject3D_2.cs
@@ -79,7 +79,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
var rebuildLocks = this.RebuilLockAll();
- return ApplicationController.Instance.Tasks.Execute(
+ return ApplicationController.Instance.Tasks.Execute(
"Combine".Localize(),
null,
(reporter, cancellationTokenSource) =>
@@ -105,10 +105,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
this.cancellationToken = null;
UiThread.RunOnIdle(() =>
{
- rebuildLocks.Dispose();
- this.CancelAllParentBuilding();
- Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
+ rebuildLocks.Dispose();
+ this.CancelAllParentBuilding();
+ Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
});
+
return Task.CompletedTask;
});
}
@@ -128,21 +129,49 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
SourceContainer.Visible = true;
RemoveAllButSource();
- var participants = SourceContainer.VisibleMeshes();
- if (participants.Count() < 2)
+ Mesh resultsMesh = null;
+ var participants = SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(this) != PrintOutputTypes.Hole);
+ if (participants.Count() == 0)
{
- 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;
}
+ else
+ {
+ resultsMesh = CombineParticipanets(reporter, participants, cancellationToken);
+ }
+ var resultsItem = new Object3D()
+ {
+ Mesh = resultsMesh
+ };
+
+ if (resultsMesh != null)
+ {
+ var holes = SourceContainer.VisibleMeshes().Where(m => m.WorldOutputType(this) == PrintOutputTypes.Hole);
+ if (holes != null)
+ {
+ var holesMesh = CombineParticipanets(null, holes, cancellationToken);
+ var holesItem = new Object3D()
+ {
+ Mesh = holesMesh
+ };
+ var resultItems = SubtractObject3D_2.DoSubtract(null,
+ new List() { resultsItem },
+ new List() { holesItem },
+ null,
+ cancellationToken);
+
+ resultsItem.Mesh = resultItems.First().Mesh;
+ }
+ }
+
+ resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
+ this.Children.Add(resultsItem);
+ SourceContainer.Visible = false;
+ }
+
+ private Mesh CombineParticipanets(IProgress reporter, IEnumerable participants, CancellationToken cancellationToken)
+ {
List> touchingSets = GetTouchingMeshes(participants);
var totalOperations = touchingSets.Sum(t => t.Count);
@@ -232,16 +261,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
- if (resultsMesh != null)
- {
- var resultsItem = new Object3D()
- {
- Mesh = resultsMesh
- };
- resultsItem.CopyProperties(participants.First(), Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
- this.Children.Add(resultsItem);
- SourceContainer.Visible = false;
- }
+ return resultsMesh;
}
private List> GetTouchingMeshes(IEnumerable participants)
diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs
index 49451015e..d990d7508 100644
--- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs
+++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractAndReplaceObject3D_2.cs
@@ -87,7 +87,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
- var parentOfSubtractTargets = this.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
@@ -195,7 +195,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
SourceContainer.Visible = true;
RemoveAllButSource();
- var parentOfPaintTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ var parentOfPaintTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
if (parentOfPaintTargets.Children.Count() < 2)
{
diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs
index b55df7c58..7d2b10e2f 100644
--- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs
+++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractObject3D_2.cs
@@ -86,7 +86,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
if (layer.Scene.SelectedItem != null
&& layer.Scene.SelectedItem == this)
{
- var parentOfSubtractTargets = this.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ var parentOfSubtractTargets = this.SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
var removeObjects = parentOfSubtractTargets.Children
.Where(i => SelectedChildren.Contains(i.ID))
@@ -109,7 +109,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
if (SelectedChildren.Count == 0)
{
- SelectedChildren.Add(SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf().Children.Last().ID);
+ SelectedChildren.Add(SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf().Children.Last().ID);
}
}
@@ -212,28 +212,29 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
Subtract(CancellationToken.None, null);
}
- private (IEnumerable, IEnumerable) GetSubtractItems()
+ private static (IEnumerable, IEnumerable) GetSubtractItems(IObject3D source, SelectedChildren selectedChildren)
{
- var parentOfSubtractTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
-
- if (parentOfSubtractTargets.Children.Count() < 2)
- {
- if (parentOfSubtractTargets.Children.Count() == 1)
- {
- this.Children.Add(SourceContainer.Clone());
- SourceContainer.Visible = false;
- }
+ 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
+ .Where((i) => selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
var keepItems = parentOfSubtractTargets.Children
- .Where((i) => !SelectedChildren
+ .Where((i) => !selectedChildren
.Contains(i.ID))
.SelectMany(c => c.VisibleMeshes());
@@ -247,98 +248,45 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
CleanUpSelectedChildrenIDs(this);
- var (keepItems, removeItems) = GetSubtractItems();
- var removeItemsCount = removeItems == null ? 0 : removeItems.Count();
- var keepItemsCount = keepItems == null ? 0 : keepItems.Count();
+ var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
- if (removeItems?.Any() == true
- && keepItems?.Any() == true)
- {
- foreach (var keep in keepItems)
+ var resultItems = DoSubtract(SourceContainer,
+ keepItems,
+ removeItems,
+ reporter,
+ cancellationToken,
+ Processing,
+ InputResolution,
+ OutputResolution);
+
+ foreach(var resultsItem in resultItems)
+ {
+ this.Children.Add(resultsItem);
+
+ if (!RemoveSubtractObjects)
{
-#if false
- var items = removeItems.Select(i => (i.Mesh, i.WorldMatrix(SourceContainer))).ToList();
- items.Insert(0, (keep.Mesh, keep.Matrix));
- var resultsMesh = BooleanProcessing.DoArray(items,
- CsgModes.Subtract,
- Processing,
- InputResolution,
- OutputResolution,
- reporter,
- cancellationToken);
-#else
- var totalOperations = removeItemsCount * keepItemsCount;
- double amountPerOperation = 1.0 / totalOperations;
- double ratioCompleted = 0;
-
- var progressStatus = new ProgressStatus
+ this.Children.Modify((list) =>
{
- Status = "Do CSG"
- };
-
- var resultsMesh = keep.Mesh;
- var keepWorldMatrix = keep.WorldMatrix(SourceContainer);
-
- foreach (var remove in removeItems)
- {
- resultsMesh = BooleanProcessing.Do(resultsMesh,
- keepWorldMatrix,
- // other mesh
- remove.Mesh,
- remove.WorldMatrix(SourceContainer),
- // operation type
- CsgModes.Subtract,
- Processing,
- InputResolution,
- OutputResolution,
- // reporting
- reporter,
- amountPerOperation,
- ratioCompleted,
- progressStatus,
- cancellationToken);
-
- // after the first time we get a result the results mesh is in the right coordinate space
- keepWorldMatrix = Matrix4X4.Identity;
-
- // report our progress
- ratioCompleted += amountPerOperation;
- progressStatus.Progress0To1 = ratioCompleted;
- reporter?.Report(progressStatus);
- }
-
-#endif
- // store our results mesh
- var resultsItem = new Object3D()
- {
- Mesh = resultsMesh,
- Visible = false,
- OwnerID = keep.ID
- };
-
- // copy all the properties but the matrix
- resultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
- // and add it to this
- this.Children.Add(resultsItem);
-
- if (!RemoveSubtractObjects)
- {
- this.Children.Modify((list) =>
+ foreach (var item in removeItems)
{
- foreach (var item in removeItems)
+ var newObject = new Object3D()
{
- var newObject = new Object3D()
- {
- Mesh = item.Mesh
- };
+ Mesh = item.Mesh
+ };
- newObject.CopyWorldProperties(item, SourceContainer, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Visible));
- list.Add(newObject);
- }
- });
- }
+ 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)
{
@@ -356,11 +304,116 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
+ public static IEnumerable DoSubtract(IObject3D sourceContainer,
+ IEnumerable keepItems,
+ IEnumerable removeItems,
+ IProgress reporter,
+ CancellationToken cancellationToken,
+ ProcessingModes processingMode = ProcessingModes.Polygons,
+ ProcessingResolution inputResolution = ProcessingResolution._64,
+ ProcessingResolution outputResolution = ProcessingResolution._64)
+ {
+ var results = new List();
+ if (keepItems?.Any() == true)
+ {
+ if (removeItems?.Any() == true)
+ {
+ foreach (var keep in keepItems)
+ {
+#if false
+ var items = removeItems.Select(i => (i.Mesh, i.WorldMatrix(sourceContainer))).ToList();
+ items.Insert(0, (keep.Mesh, keep.Matrix));
+ var resultsMesh = BooleanProcessing.DoArray(items,
+ CsgModes.Subtract,
+ processingMode,
+ inputResolution,
+ outputResolution,
+ reporter,
+ cancellationToken);
+#else
+ var totalOperations = removeItems.Count() * keepItems.Count();
+ double amountPerOperation = 1.0 / totalOperations;
+ double ratioCompleted = 0;
+
+ var progressStatus = new ProgressStatus
+ {
+ Status = "Do CSG"
+ };
+
+ var resultsMesh = keep.Mesh;
+ var keepWorldMatrix = keep.Matrix;
+ if (sourceContainer != null)
+ {
+ keepWorldMatrix = keep.WorldMatrix(sourceContainer);
+ }
+
+ foreach (var remove in removeItems)
+ {
+ var removeWorldMatrix = remove.Matrix;
+ if (sourceContainer != null)
+ {
+ removeWorldMatrix = remove.WorldMatrix(sourceContainer);
+ }
+
+ resultsMesh = BooleanProcessing.Do(resultsMesh,
+ keepWorldMatrix,
+ // other mesh
+ remove.Mesh,
+ removeWorldMatrix,
+ // operation type
+ CsgModes.Subtract,
+ processingMode,
+ inputResolution,
+ outputResolution,
+ // reporting
+ reporter,
+ amountPerOperation,
+ ratioCompleted,
+ progressStatus,
+ cancellationToken);
+
+ // after the first time we get a result the results mesh is in the right coordinate space
+ keepWorldMatrix = Matrix4X4.Identity;
+
+ // report our progress
+ ratioCompleted += amountPerOperation;
+ progressStatus.Progress0To1 = ratioCompleted;
+ reporter?.Report(progressStatus);
+ }
+
+#endif
+ // store our results mesh
+ var resultsItem = new Object3D()
+ {
+ Mesh = resultsMesh,
+ Visible = false,
+ OwnerID = keep.ID
+ };
+
+ // copy all the properties but the matrix
+ if (sourceContainer != null)
+ {
+ resultsItem.CopyWorldProperties(keep, sourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
+ }
+ else
+ {
+ resultsItem.CopyProperties(keep, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible)));
+ }
+
+ // and add it to this
+ results.Add(resultsItem);
+ }
+ }
+ }
+
+ return results;
+ }
+
public static void CleanUpSelectedChildrenIDs(OperationSourceContainerObject3D item)
{
if (item is ISelectableChildContainer selectableChildContainer)
{
- var parentOfSubtractTargets = item.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ 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
@@ -384,7 +437,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
public override string NameFromChildren()
{
- var (keepItems, removeItems) = GetSubtractItems();
+ var (keepItems, removeItems) = GetSubtractItems(SourceContainer, SelectedChildren);
return CalculateName(keepItems, ", ", " - ", removeItems, ", ");
}
}
diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs
index 799180541..733d0ec3d 100644
--- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs
+++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SubtractPathObject3D.cs
@@ -156,7 +156,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
SourceContainer.Visible = true;
RemoveAllButSource();
- var parentOfSubtractTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ var parentOfSubtractTargets = SourceContainer.FirstWithMultipleChildrenDescendantsAndSelf();
if (parentOfSubtractTargets.Children.Count() < 2)
{
@@ -243,7 +243,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
if (item is ISelectableChildContainer selectableChildContainer)
{
- var parentOfSubtractTargets = item.DescendantsAndSelfMultipleChildrenFirstOrSelf();
+ 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
diff --git a/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs b/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs
index 080bbb20c..0326c3fe1 100644
--- a/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs
+++ b/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs
@@ -704,7 +704,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
break;
case SliceSettingData.DataEditTypes.COLOR:
- uiField = new ColorField(theme, Color.Transparent, null);
+ uiField = new ColorField(theme, Color.Transparent, null, false);
break;
case SliceSettingData.DataEditTypes.POSITIVE_DOUBLE:
diff --git a/MatterControlLib/SlicerConfiguration/UIFields/ColorField.cs b/MatterControlLib/SlicerConfiguration/UIFields/ColorField.cs
index ecaf71bcb..a42b5bdfd 100644
--- a/MatterControlLib/SlicerConfiguration/UIFields/ColorField.cs
+++ b/MatterControlLib/SlicerConfiguration/UIFields/ColorField.cs
@@ -37,12 +37,14 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
public class ColorField : UIField
{
private ItemColorButton colorWidget;
+ private bool transparentCheckbox;
private Action> getPickedColor;
private ThemeConfig theme;
private Color initialColor;
- public ColorField(ThemeConfig theme, Color initialColor, Action> getPickedColor)
+ public ColorField(ThemeConfig theme, Color initialColor, Action> getPickedColor, bool transparentCheckbox)
{
+ this.transparentCheckbox = transparentCheckbox;
this.getPickedColor = getPickedColor;
this.theme = theme;
this.initialColor = initialColor;
@@ -58,7 +60,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{
var container = new FlowLayoutWidget();
- colorWidget = new ItemColorButton(theme, initialColor, getPickedColor);
+ colorWidget = new ItemColorButton(theme, initialColor, getPickedColor, transparentCheckbox);
colorWidget.ColorChanged += (s, e) =>
{
this.SetValue(Color.Html, true);
diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt
index 9e0dc0286..643b62950 100644
--- a/StaticData/Translations/Master.txt
+++ b/StaticData/Translations/Master.txt
@@ -1018,6 +1018,9 @@ Translated:Controls the speed of printer moves
English:Convert to Fuzzy Region
Translated:Convert to Fuzzy Region
+English:Convert to Hole
+Translated:Convert to Hole
+
English:Convert to Support
Translated:Convert to Support
@@ -4081,6 +4084,9 @@ Translated:Restore Settings...
English:Restoring
Translated:Restoring
+English:Result
+Translated:Result
+
English:Resume
Translated:Resume
@@ -4420,6 +4426,9 @@ Translated:Set Temperature
English:Set the information below to configure your printer. After completing this step, you can customize additional settings under the 'Settings' and 'Printer' options for this printer.
Translated:Set the information below to configure your printer. After completing this step, you can customize additional settings under the 'Settings' and 'Printer' options for this printer.
+English:Set the rendering for the object to be transparent
+Translated:Set the rendering for the object to be transparent
+
English:Sets MatterControl to attempt to connect to a printer over the network. (You must disconnect and reconnect for this to take effect)
Translated:Sets MatterControl to attempt to connect to a printer over the network. (You must disconnect and reconnect for this to take effect)
@@ -4492,6 +4501,9 @@ Translated:Share with someone
English:Shared with Me
Translated:Shared with Me
+English:Shift + G
+Translated:Shift + G
+
English:Shop
Translated:Shop
@@ -4660,6 +4672,9 @@ Translated:Snapping Turned Off
English:Software License Agreement
Translated:Software License Agreement
+English:Solid
+Translated:Solid
+
English:Solid Infill
Translated:Solid Infill
diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp
index 3d0bfff6b..365d3ed68 160000
--- a/Submodules/agg-sharp
+++ b/Submodules/agg-sharp
@@ -1 +1 @@
-Subproject commit 3d0bfff6b8e76cab9e732fc2565863d7dd2ee5e5
+Subproject commit 365d3ed687c1502e0d5f12d36ae79b03b92ba9e0
diff --git a/Tests/MatterControl.AutomationTests/PartPreviewTests.cs b/Tests/MatterControl.AutomationTests/PartPreviewTests.cs
index 34f8a0b0c..59c8fabf1 100644
--- a/Tests/MatterControl.AutomationTests/PartPreviewTests.cs
+++ b/Tests/MatterControl.AutomationTests/PartPreviewTests.cs
@@ -420,7 +420,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
}
[Test]
- public async Task DesignTabFileOpperations()
+ public async Task DesignTabFileOperations()
{
await MatterControlUtilities.RunTest((testRunner) =>
{