Working on adding hole support to Source Objects
This commit is contained in:
parent
16af8dcdb2
commit
ce9019884f
11 changed files with 599 additions and 125 deletions
|
|
@ -677,16 +677,11 @@ namespace MatterHackers.MatterControl
|
|||
return new SceneOperation("Align")
|
||||
{
|
||||
OperationType = typeof(IObject3D),
|
||||
ResultType = typeof(AlignObject3D_2),
|
||||
ResultType = typeof(AlignObject3D_3),
|
||||
TitleGetter = () => "Align".Localize(),
|
||||
Action = (sceneContext) =>
|
||||
{
|
||||
var scene = sceneContext.Scene;
|
||||
var selectedItem = scene.SelectedItem;
|
||||
var align = new AlignObject3D_2();
|
||||
align.AddSelectionAsChildren(scene, selectedItem);
|
||||
align.Name = align.NameFromChildren();
|
||||
align.NameOverriden = false;
|
||||
new AlignObject3D_3().WrapSelectedItemAndSelect(sceneContext.Scene);
|
||||
},
|
||||
Icon = (theme) => StaticData.Instance.LoadIcon("align_left_dark.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(),
|
||||
HelpTextGetter = () => "At least 2 parts must be selected".Localize().Stars(),
|
||||
|
|
|
|||
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, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
}
|
||||
|
||||
public static GuiWidget NewColorSelector(ThemeConfig theme,
|
||||
Color selectedColor,
|
||||
Color startingColor,
|
||||
ThemeConfig menuTheme,
|
||||
Action<Color> update,
|
||||
Action<Action<Color>> getPickedColor,
|
||||
|
|
@ -120,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,
|
||||
|
|
@ -147,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) =>
|
||||
{
|
||||
|
|
@ -162,7 +162,7 @@ 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
|
||||
{
|
||||
|
|
@ -221,7 +221,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
HAnchor = HAnchor.Fit | HAnchor.Left,
|
||||
VAnchor = VAnchor.Absolute,
|
||||
ToolTipText = "Set the rendering for the object to be transparent".Localize(),
|
||||
Checked = selectedColor.Alpha0To255 < 255,
|
||||
Checked = startingColor.Alpha0To255 < 255,
|
||||
};
|
||||
|
||||
rightContent.AddChild(transparent);
|
||||
|
|
|
|||
|
|
@ -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, true);
|
||||
var colorField = new ColorField(theme, worldColor, GetNextSelectionColor, true);
|
||||
colorField.Initialize(0);
|
||||
colorField.ValueChanged += (s, e) =>
|
||||
{
|
||||
|
|
@ -311,7 +318,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
}
|
||||
|
||||
var currentOutputType = selectedItem.WorldOutputType();
|
||||
if (currentOutputType != PrintOutputTypes.Solid || currentOutputType != PrintOutputTypes.Default)
|
||||
if (currentOutputType != PrintOutputTypes.Solid && currentOutputType != PrintOutputTypes.Default)
|
||||
{
|
||||
undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,98 @@ 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.WorldMatrix(sourceContainer);
|
||||
|
||||
foreach (var remove in removeItems)
|
||||
{
|
||||
resultsMesh = BooleanProcessing.Do(resultsMesh,
|
||||
keepWorldMatrix,
|
||||
// other mesh
|
||||
remove.Mesh,
|
||||
remove.WorldMatrix(sourceContainer),
|
||||
// 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
|
||||
resultsItem.CopyWorldProperties(keep, sourceContainer, 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 +419,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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 3d0bfff6b8e76cab9e732fc2565863d7dd2ee5e5
|
||||
Subproject commit 8d5c8129f1ab281e613a7eef0f05a24074371d90
|
||||
Loading…
Add table
Add a link
Reference in a new issue