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) => {