commit
e95df616f8
22 changed files with 858 additions and 176 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
434
MatterControlLib/DesignTools/Operations/AlignObject3D_3.cs
Normal file
434
MatterControlLib/DesignTools/Operations/AlignObject3D_3.cs
Normal file
|
|
@ -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, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -331,6 +331,8 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
list.AddRange(twistedChildren);
|
||||
});
|
||||
|
||||
ApplyHoles(reporter, cancellationToken.Token);
|
||||
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
rebuildLocks.Dispose();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="reporter">Show progress</param>
|
||||
/// <param name="cancellationToken">Can check if the operation has been canceled</param>
|
||||
/// <returns>Did any holes get subtracted</returns>
|
||||
public bool ApplyHoles(IProgress<ProgressStatus> 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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Action<Color>> getPickedColor)
|
||||
private static int TransparentAmount => 120;
|
||||
|
||||
public ItemColorButton(ThemeConfig theme, Color selectedColor, Action<Action<Color>> 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<Color> update,
|
||||
Action<Action<Color>> getPickedColor)
|
||||
Action<Action<Color>> 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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<ColorButton>().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<FlowLayoutWidget>().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);
|
||||
|
|
|
|||
|
|
@ -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<IObject3D>() { resultsItem },
|
||||
new List<IObject3D>() { 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<ProgressStatus> reporter, IEnumerable<IObject3D> participants, CancellationToken cancellationToken)
|
||||
{
|
||||
List<List<(Mesh mesh, Matrix4X4 matrix, AxisAlignedBoundingBox aabb)>> 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<List<(Mesh mesh, Matrix4X4 matrix, AxisAlignedBoundingBox aabb)>> GetTouchingMeshes(IEnumerable<IObject3D> participants)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<IObject3D>, IEnumerable<IObject3D>) GetSubtractItems()
|
||||
private static (IEnumerable<IObject3D>, IEnumerable<IObject3D>) 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<IObject3D> DoSubtract(IObject3D sourceContainer,
|
||||
IEnumerable<IObject3D> keepItems,
|
||||
IEnumerable<IObject3D> removeItems,
|
||||
IProgress<ProgressStatus> reporter,
|
||||
CancellationToken cancellationToken,
|
||||
ProcessingModes processingMode = ProcessingModes.Polygons,
|
||||
ProcessingResolution inputResolution = ProcessingResolution._64,
|
||||
ProcessingResolution outputResolution = ProcessingResolution._64)
|
||||
{
|
||||
var results = new List<IObject3D>();
|
||||
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, ", ");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -37,12 +37,14 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
|
|||
public class ColorField : UIField
|
||||
{
|
||||
private ItemColorButton colorWidget;
|
||||
private bool transparentCheckbox;
|
||||
private Action<Action<Color>> getPickedColor;
|
||||
private ThemeConfig theme;
|
||||
private Color initialColor;
|
||||
|
||||
public ColorField(ThemeConfig theme, Color initialColor, Action<Action<Color>> getPickedColor)
|
||||
public ColorField(ThemeConfig theme, Color initialColor, Action<Action<Color>> 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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 3d0bfff6b8e76cab9e732fc2565863d7dd2ee5e5
|
||||
Subproject commit 365d3ed687c1502e0d5f12d36ae79b03b92ba9e0
|
||||
|
|
@ -420,7 +420,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
|
|||
}
|
||||
|
||||
[Test]
|
||||
public async Task DesignTabFileOpperations()
|
||||
public async Task DesignTabFileOperations()
|
||||
{
|
||||
await MatterControlUtilities.RunTest((testRunner) =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue