Merge branch '2.19.2'

This commit is contained in:
LarsBrubaker 2019-02-03 13:34:11 -08:00
commit 6731fdc5d8
168 changed files with 13231 additions and 7124 deletions

View file

@ -50,27 +50,6 @@ namespace MatterHackers.MatterControl
public static class CreateDiscreteMeshes
{
public static List<List<int>> GetFacesSharingVertex(List<Vector3Float> vertexList, FaceList faces)
{
var sharingList = new List<List<int>>(vertexList.Count);
for(int i=0; i<vertexList.Count; i++)
{
sharingList.Add(new List<int>());
}
for (int faceIndex = 0; faceIndex < faces.Count; faceIndex++)
{
var face = faces[faceIndex];
var vertices = new int[] { face.v0, face.v1, face.v2 };
for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++)
{
sharingList[vertexIndex].Add(faceIndex);
}
}
return sharingList;
}
public static List<Mesh> SplitVolumesIntoMeshes(Mesh meshToSplit, CancellationToken cancellationToken, Action<double, string> reportProgress)
{
Stopwatch maxProgressReport = Stopwatch.StartNew();
@ -80,8 +59,8 @@ namespace MatterHackers.MatterControl
var attachedFaces = new Stack<int>();
int faceCount = meshToSplit.Faces.Count;
var facesSharingVertex = GetFacesSharingVertex(meshToSplit.Vertices, meshToSplit.Faces);
var facesSharingVertex = meshToSplit.NewVertexFaceLists();
for (int faceIndex = 0; faceIndex < faceCount; faceIndex++)
{
if (reportProgress != null)
@ -110,7 +89,7 @@ namespace MatterHackers.MatterControl
foreach (var attachedVertex in vertices)
{
foreach (var sharedFaceIndex in facesSharingVertex[attachedVertex])
foreach (var sharedFaceIndex in facesSharingVertex[attachedVertex].Faces)
{
if (!facesThatHaveBeenAdded.Contains(sharedFaceIndex))
{

View file

@ -27,9 +27,8 @@ 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.Linq;
using System.Reflection;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
@ -44,30 +43,17 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
[HideFromTreeViewAttribute, Immutable]
public class GeneratedSupportObject3D : Object3D
{
public GeneratedSupportObject3D()
{
OutputType = PrintOutputTypes.Support;
}
}
public class GenerateSupportPanel : FlowLayoutWidget
{
/// <summary>
/// The amount to reduce the pillars so they are separated in the 3D view
/// </summary>
private double reduceAmount => PillarSize / 8;
private InteractiveScene scene;
private SupportGenerator supportGenerator;
private ThemeConfig theme;
public GenerateSupportPanel(ThemeConfig theme, InteractiveScene scene)
: base(FlowDirection.TopToBottom)
{
supportGenerator = new SupportGenerator(scene);
this.theme = theme;
this.scene = scene;
this.VAnchor = VAnchor.Fit;
this.HAnchor = HAnchor.Absolute;
@ -75,34 +61,57 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
this.BackgroundColor = theme.BackgroundColor;
this.Padding = theme.DefaultContainerPadding;
// put in support pillar size
// Add an editor field for the SupportGenerator.SupportType
PropertyInfo propertyInfo = typeof(SupportGenerator).GetProperty(nameof(SupportGenerator.SupportType));
// support pillar resolution
var editor = PublicPropertyEditor.CreatePropertyEditor(
new EditableProperty(propertyInfo, supportGenerator),
null,
new PPEContext(),
theme);
if (editor != null)
{
this.AddChild(editor);
}
// put in support pillar size
var pillarSizeField = new DoubleField(theme);
pillarSizeField.Initialize(0);
pillarSizeField.DoubleValue = PillarSize;
pillarSizeField.DoubleValue = supportGenerator.PillarSize;
pillarSizeField.ValueChanged += (s, e) =>
{
PillarSize = pillarSizeField.DoubleValue;
supportGenerator.PillarSize = pillarSizeField.DoubleValue;
// in case it was corrected set it back again
if (pillarSizeField.DoubleValue != supportGenerator.PillarSize)
{
pillarSizeField.DoubleValue = supportGenerator.PillarSize;
}
};
var pillarRow = PublicPropertyEditor.CreateSettingsRow("Pillar Size".Localize(), "The width and depth of the support pillars".Localize());
var pillarRow = PublicPropertyEditor.CreateSettingsRow("Pillar Size".Localize(), "The width and depth of the support pillars".Localize(), theme);
pillarRow.AddChild(pillarSizeField.Content);
this.AddChild(pillarRow);
// put in the angle setting
var overHangField = new DoubleField(theme);
overHangField.Initialize(0);
overHangField.DoubleValue = MaxOverHangAngle;
overHangField.DoubleValue = supportGenerator.MaxOverHangAngle;
overHangField.ValueChanged += (s, e) =>
{
MaxOverHangAngle = overHangField.DoubleValue;
supportGenerator.MaxOverHangAngle = overHangField.DoubleValue;
// in case it was corrected set it back again
if (overHangField.DoubleValue != supportGenerator.MaxOverHangAngle)
{
overHangField.DoubleValue = supportGenerator.MaxOverHangAngle;
}
};
var overHangRow = PublicPropertyEditor.CreateSettingsRow("Overhang Angle".Localize(), "The angle to generate support for".Localize());
var overHangRow = PublicPropertyEditor.CreateSettingsRow("Overhang Angle".Localize(), "The angle to generate support for".Localize(), theme);
overHangRow.AddChild(overHangField.Content);
this.AddChild(overHangRow);
// Button Row
var buttonRow = new FlowLayoutWidget()
{
HAnchor = HAnchor.Stretch,
@ -116,7 +125,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// add 'Remove Auto Supports' button
var removeButton = theme.CreateDialogButton("Remove".Localize());
removeButton.ToolTipText = "Remove all auto generated supports".Localize();
removeButton.Click += (s, e) => RemoveExisting();
removeButton.Click += (s, e) => supportGenerator.RemoveExisting();
buttonRow.AddChild(removeButton);
// add 'Generate Supports' button
@ -127,239 +136,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
theme.ApplyPrimaryActionStyle(generateButton);
}
public static double MaxOverHangAngle { get; private set; } = 45;
public double PillarSize { get; private set; } = 4;
private void AddSupportColumn(IObject3D holder, double gridX, double gridY, double bottomZ, double topZ)
{
if(topZ - bottomZ < .01)
{
// less than 10 micros high, don't ad it
return;
}
holder.Children.Add(new GeneratedSupportObject3D()
{
Mesh = PlatonicSolids.CreateCube(PillarSize - reduceAmount, PillarSize - reduceAmount, topZ - bottomZ),
Matrix = Matrix4X4.CreateTranslation(gridX, gridY, bottomZ + (topZ - bottomZ) / 2)
});
}
private Task Rebuild()
{
return ApplicationController.Instance.Tasks.Execute(
"Create Support".Localize(),
null,
(reporter, cancellationToken) =>
{
// Get visible meshes for each of them
var visibleMeshes = scene.Children.SelectMany(i => i.VisibleMeshes());
var selectedItem = scene.SelectedItem;
if (selectedItem != null)
{
visibleMeshes = selectedItem.VisibleMeshes();
}
var supportCandidates = visibleMeshes.Where(i => i.OutputType != PrintOutputTypes.Support);
// find all the faces that are candidates for support
var upVerts = new List<Vector3Float>();
var upFaces = new FaceList();
var downVerts = new List<Vector3Float>();
var downFaces = new FaceList();
foreach (var item in supportCandidates)
{
var matrix = item.WorldMatrix(scene);
for (int faceIndex = 0; faceIndex < item.Mesh.Faces.Count; faceIndex++)
{
var face0Normal = item.Mesh.Faces[faceIndex].normal.TransformNormal(matrix).GetNormal();
var angle = MathHelper.RadiansToDegrees(Math.Acos(face0Normal.Dot(-Vector3Float.UnitZ)));
if (angle < MaxOverHangAngle)
{
var face = item.Mesh.Faces[faceIndex];
var verts = new int[] { face.v0, face.v1, face.v2 };
var fc = downVerts.Count;
downVerts.Add(item.Mesh.Vertices[face.v0].Transform(matrix));
downVerts.Add(item.Mesh.Vertices[face.v1].Transform(matrix));
downVerts.Add(item.Mesh.Vertices[face.v2].Transform(matrix));
downFaces.Add(fc, fc + 1, fc + 2, downVerts);
}
if (angle > 0)
{
var face = item.Mesh.Faces[faceIndex];
var verts = new int[] { face.v0, face.v1, face.v2 };
var fc = upFaces.Count;
upVerts.Add(item.Mesh.Vertices[face.v0].Transform(matrix));
upVerts.Add(item.Mesh.Vertices[face.v1].Transform(matrix));
upVerts.Add(item.Mesh.Vertices[face.v2].Transform(matrix));
upFaces.Add(fc, fc + 1, fc + 2, upVerts);
}
}
}
if (downFaces.Count > 0)
{
var downTraceData = downFaces.CreateTraceData(downVerts, 0);
var upTraceData = upFaces.CreateTraceData(upVerts, 0);
// get the bounds of all verts
var bounds = downVerts.Bounds();
// create the gird of possible support
var gridBounds = new RectangleDouble(Math.Floor((double)(bounds.MinXYZ.X / PillarSize)),
Math.Floor((double)(bounds.MinXYZ.Y / PillarSize)),
Math.Ceiling(bounds.MaxXYZ.X / PillarSize),
Math.Ceiling(bounds.MaxXYZ.Y / PillarSize));
var supportGrid = new List<List<double>>((int)(gridBounds.Width * gridBounds.Height));
IObject3D holder = new Object3D();
// at the center of every grid item add in a list of all the top faces to look down from
for (int y = 0; y < gridBounds.Height; y++)
{
var yPos = (gridBounds.Bottom + y) * PillarSize;
for (int x = 0; x < gridBounds.Width; x++)
{
var xPos = (gridBounds.Left + x) * PillarSize;
IntersectInfo upHit = null;
var upRay = new Ray(new Vector3(xPos, yPos, 0), Vector3.UnitZ, intersectionType: IntersectionType.Both);
do
{
upHit = downTraceData.GetClosestIntersection(upRay);
if (upHit != null)
{
// we found a ceiling above this spot, look down from that to find the first floor
var downRay = new Ray(new Vector3(upHit.HitPosition.X, upHit.HitPosition.Y, upHit.HitPosition.Z - .001), -Vector3.UnitZ, intersectionType: IntersectionType.Both);
var downHit = upTraceData.GetClosestIntersection(downRay);
if (downHit != null)
{
AddSupportColumn(holder, downHit.HitPosition.X, downHit.HitPosition.Y, downHit.HitPosition.Z, upHit.HitPosition.Z);
}
else
{
// did not find a hit, go to the bed
AddSupportColumn(holder, upHit.HitPosition.X, upHit.HitPosition.Y, upRay.origin.Z, upHit.HitPosition.Z);
}
// make a new ray just past the last hit to keep looking for up hits
upRay = new Ray(new Vector3(xPos, yPos, upHit.HitPosition.Z + .001), Vector3.UnitZ, intersectionType: IntersectionType.Both);
}
} while (upHit != null);
}
}
// foreach face set the support heights in the overlapped support grid
// foreach grid column that has data
// trace down from the top to the first bottom hit (or bed)
// add a support column
var first = downFaces.First();
var position = downVerts[first.v0];
//AddSupportColumn(position.X, position.Y, position.Z, 0);
scene.Children.Modify(list =>
{
list.AddRange(holder.Children);
});
}
// this is the theory for regions rather than pillars
// separate the faces into face patch groups (these are the new support tops)
// project all the vertices of each patch group down until they hit an up face in the scene (or 0)
// make a new patch group at the z of the hit (these will be the bottoms)
// find the outline of the patch groups (these will be the walls of the top and bottom patches
// make a new mesh object with the top, bottom and walls, add it to the scene and mark it as support
return Task.CompletedTask;
});
}
private void RemoveExisting()
{
var existingSupports = scene.Children.Where(i => i.GetType() == typeof(GeneratedSupportObject3D));
scene.Children.Modify((list) =>
{
foreach (var item in existingSupports)
{
list.Remove(item);
}
});
}
public static bool RequiresSupport(InteractiveScene scene)
{
bool supportInScene = scene.VisibleMeshes().Any(i => i.WorldOutputType() == PrintOutputTypes.Support);
if (!supportInScene)
{
// there is no support in the scene check if there are faces that require support
var supportCandidates = scene.VisibleMeshes().Where(i => i.OutputType != PrintOutputTypes.Support);
// find all the faces that are candidates for support
foreach (var item in supportCandidates)
{
var matrix = item.WorldMatrix(scene);
for (int faceIndex = 0; faceIndex < item.Mesh.Faces.Count; faceIndex++)
{
bool aboveBed = false;
var face = item.Mesh.Faces[faceIndex];
var verts = new int[] { face.v0, face.v1, face.v2 };
foreach(var vertex in verts)
{
if(item.Mesh.Vertices[vertex].Transform(matrix).Z > .01)
{
aboveBed = true;
break;
}
}
if (aboveBed)
{
var face0Normal = item.Mesh.Faces[faceIndex].normal.TransformNormal(matrix).GetNormal();
var angle = MathHelper.RadiansToDegrees(Math.Acos(face0Normal.Dot(-Vector3Float.UnitZ)));
if (angle < MaxOverHangAngle)
{
// TODO: consider how much area all supported polygons represent
return true;
}
}
}
}
}
return false;
}
}
public static class FaceListExtensions
{
public static IPrimitive CreateTraceData(this FaceList faceList, List<Vector3> vertexList, int maxRecursion = int.MaxValue)
{
var allPolys = new List<IPrimitive>();
foreach (var face in faceList)
{
allPolys.Add(new TriangleShape(vertexList[face.v0], vertexList[face.v1], vertexList[face.v2], null));
}
return BoundingVolumeHierarchy.CreateNewHierachy(allPolys, maxRecursion);
}
public static IPrimitive CreateTraceData(this FaceList faceList, List<Vector3Float> vertexList, int maxRecursion = int.MaxValue)
{
var allPolys = new List<IPrimitive>();
foreach (var face in faceList)
{
allPolys.Add(new TriangleShape(vertexList[face.v0], vertexList[face.v1], vertexList[face.v2], null));
}
return BoundingVolumeHierarchy.CreateNewHierachy(allPolys, maxRecursion);
null, supportGenerator.Create
);
}
}
}

View file

@ -98,28 +98,36 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// Force common padding into top region
tabControl.TabBar.Padding = theme.TabbarPadding.Clone(top: theme.TabbarPadding.Top * 2, bottom: 0);
// add in the update available button
updateAvailableButton = new LinkLabel("Update Available".Localize(), theme)
if (Application.EnableNetworkTraffic)
{
Visible = false,
Name = "Update Available Link",
ToolTipText = "There is a new update available for download".Localize(),
VAnchor = VAnchor.Center,
Margin = new BorderDouble(10, 0)
};
// add in the update available button
updateAvailableButton = new LinkLabel("Update Available".Localize(), theme)
{
Visible = false,
Name = "Update Available Link",
ToolTipText = "There is a new update available for download".Localize(),
VAnchor = VAnchor.Center,
Margin = new BorderDouble(10, 0)
};
// Register listeners
UserSettings.Instance.SettingChanged += SetLinkButtonsVisibility;
// Register listeners
UserSettings.Instance.SettingChanged += SetLinkButtonsVisibility;
SetLinkButtonsVisibility(this, null);
SetLinkButtonsVisibility(this, null);
updateAvailableButton.Click += (s, e) => UiThread.RunOnIdle(() =>
{
UpdateControlData.Instance.CheckForUpdate();
DialogWindow.Show<CheckForUpdatesPage>();
});
updateAvailableButton.Click += (s, e) => UiThread.RunOnIdle(() =>
{
UpdateControlData.Instance.CheckForUpdate();
DialogWindow.Show<CheckForUpdatesPage>();
});
tabControl.TabBar.ActionArea.AddChild(updateAvailableButton);
tabControl.TabBar.ActionArea.AddChild(updateAvailableButton);
UpdateControlData.Instance.UpdateStatusChanged.RegisterEvent((s, e) =>
{
SetLinkButtonsVisibility(s, new StringEventArgs("Unknown"));
}, ref unregisterEvents);
}
this.AddChild(tabControl);
@ -266,11 +274,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
ApplicationController.Instance.ShellFileOpened += this.Instance_OpenNewFile;
UpdateControlData.Instance.UpdateStatusChanged.RegisterEvent((s, e) =>
{
SetLinkButtonsVisibility(s, new StringEventArgs("Unknown"));
}, ref unregisterEvents);
ApplicationController.Instance.MainView = this;
}

View file

@ -139,10 +139,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
switch (item)
{
case TransformWrapperObject3D fitToBounds3D:
case TransformWrapperObject3D transformWrapperObject3D:
return new ObjectView()
{
Children = new IObject3D[] { fitToBounds3D.SourceItem },
Children = transformWrapperObject3D.SourceItems,
Name = item.Name,
Source = item
};
@ -172,6 +172,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
Source = item
};
case OperationSourceContainerObject3D operationSourceContainerObject3D:
return new ObjectView()
{
Children = item.Children.OfType<OperationSourceObject3D>().ToList(),
Name = operationSourceContainerObject3D.Name,
Source = item
};
default:
return new ObjectView(item);
}

View file

@ -130,16 +130,6 @@ namespace MatterHackers.MatterControl
object3D.Matrix *= Matrix4X4.CreateTranslation(new Vector3(0, 0, -boundsCenter.Z + bounds.ZSize / 2));
}
public static void PlaceMeshAtHeight(IObject3D objectToMove, double zHeight)
{
AxisAlignedBoundingBox bounds = objectToMove.GetAxisAlignedBoundingBox(Matrix4X4.Identity);
if (bounds.MinXYZ.Z != zHeight)
{
objectToMove.Matrix *= Matrix4X4.CreateTranslation(new Vector3(0, 0, zHeight - bounds.MinXYZ.Z));
}
}
/// <summary>
/// Moves the target object to the first non-colliding position, starting from the lower left corner of the bounding box containing all sceneItems
/// </summary>

View file

@ -75,10 +75,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
public ArrowDirection ArrowDirection
{
get => _arrow;
protected set
set
{
_arrow = value;
this.RebuildShape();
switch (_arrow)
{
case ArrowDirection.Top:

View file

@ -641,7 +641,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
if (doSlicing)
{
var errors = printer.ValidateSettings();
if(errors.Count > 0)
if(errors.Any(err => err.ErrorLevel == ValidationErrorLevel.Error))
{
doSlicing = false;
ApplicationController.Instance.ShowValidationErrors("Slicing Error".Localize(), errors);

View file

@ -90,14 +90,17 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
if (this.item.CanFlatten)
{
this.item.Flatten(view3DWidget.Scene.UndoBuffer);
var item = this.item;
using (new SelectionMaintainer(view3DWidget.Scene))
{
item.Flatten(view3DWidget.Scene.UndoBuffer);
}
}
else
{
// try to ungroup it
sceneContext.Scene.UngroupSelection();
}
scene.SelectedItem = null;
};
toolbar.AddChild(flattenButton);
@ -110,17 +113,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
};
removeButton.Click += (s, e) =>
{
var rootSelection = scene.SelectedItemRoot;
var removeItem = item;
removeItem.Remove(view3DWidget.Scene.UndoBuffer);
scene.SelectedItem = null;
// Only restore the root selection if it wasn't the item removed
if (removeItem != rootSelection)
var item = this.item;
using (new SelectionMaintainer(view3DWidget.Scene))
{
scene.SelectedItem = rootSelection;
item.Remove(view3DWidget.Scene.UndoBuffer);
}
};
toolbar.AddChild(removeButton);
@ -221,7 +217,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var button = new IconButton(primaryAction.IconCollector(theme), theme)
{
//Name = namedAction.Title + " Button",
//ToolTipText = namedAction.Title,
ToolTipText = primaryAction.Title,
Margin = theme.ButtonSpacing,
BackgroundColor = theme.ToolbarButtonBackground,
HoverColor = theme.ToolbarButtonHover,
@ -447,6 +443,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|| selectedItem.CanFlatten);
removeButton.Enabled = selectedItem != null;
overflowButton.Enabled = selectedItem != null;
if(selectedItem == null)
{
primaryActionsPanel.RemoveAllChildren();
}
}
}
}

View file

@ -33,6 +33,7 @@ using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
@ -87,6 +88,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
public static void ShowPopover(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), double secondsToClose = 0)
{
var settingsRow = anchor.Widget as SettingsRow;
var popoverWidget = popup.Widget as SliceSettingsPopover;
var hookedWidgets = new HashSet<GuiWidget>();
void anchor_Closed(object sender, EventArgs e)
{
@ -127,7 +131,22 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
if (screenPosition.X + popup.Widget.Width > systemWindow.Width
|| screenPosition.X < 0)
{
xPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
var altXPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
var altScreenPosition = anchorLeft + altXPosition;
// Prefer clipping on edge revealed by resize
if ((popup.AltMate.Right && altScreenPosition.X > -15)
|| (popup.AltMate.Left && altScreenPosition.X + popup.Widget.Width < systemWindow.Width))
{
xPosition = altXPosition;
if (settingsRow != null
&& popoverWidget != null)
{
popoverWidget.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Left ? ArrowDirection.Right : ArrowDirection.Left;
}
}
}
popupPosition += xPosition;
@ -142,6 +161,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|| screenPosition.Y < 0))
{
yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
if (settingsRow != null)
{
settingsRow.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Top ? ArrowDirection.Bottom: ArrowDirection.Top;
}
}
popup.Widget.Closed += anchor_Closed;

View file

@ -28,52 +28,30 @@ either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.PolygonMesh;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class CombineObject3D : MeshWrapperObject3D
public class CombineObject3D_2 : OperationSourceContainerObject3D
{
public CombineObject3D()
public CombineObject3D_2()
{
Name = "Combine";
}
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType == InvalidateType.Content
|| invalidateType.InvalidateType == InvalidateType.Matrix
|| invalidateType.InvalidateType == InvalidateType.Mesh)
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
else if (invalidateType.InvalidateType == InvalidateType.Properties
&& invalidateType.Source == this)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Combine".Localize(),
null,
@ -90,12 +68,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
base.Invalidate(new InvalidateArgs(this, InvalidateType.Content));
});
rebuildLocks.Dispose();
Invalidate(InvalidateType.Children);
return Task.CompletedTask;
});
}
@ -107,15 +81,26 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
public void Combine(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
SourceContainer.Visible = true;
RemoveAllButSource();
var participants = this.Descendants().Where(o => o.OwnerID == this.ID).ToList();
var participants = SourceContainer.VisibleMeshes();
if (participants.Count() < 2)
{
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;
}
var first = participants.First();
var resultsMesh = first.Mesh;
var firstWorldMatrix = first.WorldMatrix(SourceContainer);
var totalOperations = participants.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
@ -126,25 +111,27 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
if (item != first)
{
var result = BooleanProcessing.Do(item.Mesh, item.WorldMatrix(),
first.Mesh, first.WorldMatrix(),
0,
var itemWorldMatrix = item.WorldMatrix(SourceContainer);
resultsMesh = BooleanProcessing.Do(item.Mesh, itemWorldMatrix,
resultsMesh, firstWorldMatrix,
0,
reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverse = first.WorldMatrix();
inverse.Invert();
result.Transform(inverse);
using (first.RebuildLock())
{
first.Mesh = result;
}
item.Visible = false;
// after the first union we are working with the transformed mesh and don't need the first transform
firstWorldMatrix = Matrix4X4.Identity;
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
}
var resultsItem = new Object3D()
{
Mesh = resultsMesh
};
resultsItem.CopyProperties(first, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem);
SourceContainer.Visible = false;
}
}
}

View file

@ -115,7 +115,7 @@ namespace MatterHackers.MatterControl.DesignTools
thumbnailWidget.Image = SetImage(theme, imageObject);
column.Invalidate();
imageObject.Invalidate(new InvalidateArgs(imageObject, InvalidateType.Image));
imageObject.Invalidate(InvalidateType.Image);
};
pasteMenu.Enabled = Clipboard.Instance.ContainsImage;
@ -172,7 +172,7 @@ namespace MatterHackers.MatterControl.DesignTools
thumbnailWidget.Image = SetImage(theme, imageObject);
column.Invalidate();
imageObject.Invalidate(new InvalidateArgs(imageObject, InvalidateType.Image));
imageObject.Invalidate(InvalidateType.Image);
});
});
};
@ -202,7 +202,7 @@ namespace MatterHackers.MatterControl.DesignTools
imageObject.Invalidated += (s, e) =>
{
if (e.InvalidateType == InvalidateType.Image
if (e.InvalidateType.HasFlag(InvalidateType.Image)
&& activeImage != imageObject.Image)
{
thumbnailImage = SetImage(theme, imageObject);

View file

@ -1,142 +0,0 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.PolygonMesh;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class IntersectionObject3D : MeshWrapperObject3D
{
public IntersectionObject3D()
{
Name = "Intersection";
}
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType == InvalidateType.Content
|| invalidateType.InvalidateType == InvalidateType.Matrix
|| invalidateType.InvalidateType == InvalidateType.Mesh)
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
else if (invalidateType.InvalidateType == InvalidateType.Properties
&& invalidateType.Source == this)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute("Intersection".Localize(), null, (reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Intersect(cancellationToken, reporter);
}
catch { }
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
base.Invalidate(new InvalidateArgs(this, InvalidateType.Content));
});
return Task.CompletedTask;
});
}
public void Intersect()
{
Intersect(CancellationToken.None, null);
}
private void Intersect(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
ResetMeshWrapperMeshes(Object3DPropertyFlags.All, cancellationToken);
var participants = this.DescendantsAndSelf().Where((obj) => obj.OwnerID == this.ID);
if (participants.Count() > 1)
{
var first = participants.First();
var totalOperations = participants.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
ProgressStatus progressStatus = new ProgressStatus();
foreach (var remove in participants)
{
if (remove != first)
{
var result = BooleanProcessing.Do(remove.Mesh, remove.WorldMatrix(),
first.Mesh, first.WorldMatrix(),
2, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverse = first.WorldMatrix();
inverse.Invert();
result.Transform(inverse);
using (first.RebuildLock())
{
first.Mesh = result;
}
remove.Visible = false;
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter.Report(progressStatus);
}
}
}
}
}
}

View file

@ -0,0 +1,138 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class IntersectionObject3D_2 : OperationSourceContainerObject3D
{
public IntersectionObject3D_2()
{
Name = "Intersection";
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
return ApplicationController.Instance.Tasks.Execute(
"Intersection".Localize(),
null,
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Intersect(cancellationToken, reporter);
}
catch
{
}
rebuildLocks.Dispose();
Invalidate(InvalidateType.Children);
return Task.CompletedTask;
});
}
public void Intersect()
{
Intersect(CancellationToken.None, null);
}
private void Intersect(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var participants = SourceContainer.VisibleMeshes();
if (participants.Count() < 2)
{
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;
}
var first = participants.First();
var resultsMesh = first.Mesh;
var firstWorldMatrix = first.WorldMatrix(SourceContainer);
var totalOperations = participants.Count() - 1;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
ProgressStatus progressStatus = new ProgressStatus();
foreach (var item in participants)
{
if (item != first)
{
var itemWorldMatrix = item.WorldMatrix(SourceContainer);
resultsMesh = BooleanProcessing.Do(item.Mesh, itemWorldMatrix,
resultsMesh, firstWorldMatrix,
2,
reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
// after the first union we are working with the transformed mesh and don't need the first transform
firstWorldMatrix = Matrix4X4.Identity;
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
}
var resultsItem = new Object3D()
{
Mesh = resultsMesh
};
resultsItem.CopyProperties(first, Object3DPropertyFlags.All & (~Object3DPropertyFlags.Matrix));
this.Children.Add(resultsItem);
SourceContainer.Visible = false;
}
}
}

View file

@ -122,7 +122,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
Invalidate(new InvalidateArgs(this, InvalidateType.Content, undoBuffer));
Invalidate(InvalidateType.Children);
}
/// <summary>
@ -187,7 +187,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
// collapse our children into our parent
// and replace us with the children
var replaceCommand = new ReplaceCommand(new List<IObject3D> { this }, thisClone.Children.ToList());
var replaceCommand = new ReplaceCommand(new[] { this }, thisClone.Children.ToList());
if (undoBuffer != null)
{
undoBuffer.AddAndDo(replaceCommand);
@ -199,7 +199,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
Invalidate(new InvalidateArgs(this, InvalidateType.Content));
Invalidate(InvalidateType.Children);
}
public void ResetMeshWrapperMeshes(Object3DPropertyFlags flags, CancellationToken cancellationToken)
@ -266,7 +266,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
Invalidate(new InvalidateArgs(this, InvalidateType.Properties, null));
Invalidate(InvalidateType.Properties);
}
public void WrapItems(List<IObject3D> items)
@ -290,7 +290,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
this.MakeNameNonColliding();
}
Invalidate(new InvalidateArgs(this, InvalidateType.Properties, null));
Invalidate(InvalidateType.Properties);
}
private void AddMeshWrapperToAllChildren()

View file

@ -49,12 +49,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
var firstChild = this.Children.FirstOrDefault();
if (firstChild != null)
{
if (invalidateType.InvalidateType == InvalidateType.Color)
if (invalidateType.InvalidateType.HasFlag(InvalidateType.Color))
{
this.Color = firstChild.Color;
}
if (invalidateType.InvalidateType == InvalidateType.Material)
if (invalidateType.InvalidateType.HasFlag(InvalidateType.Material))
{
this.MaterialIndex = firstChild.MaterialIndex;
}

View file

@ -57,23 +57,23 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType == InvalidateType.Content
|| invalidateType.InvalidateType == InvalidateType.Matrix
|| invalidateType.InvalidateType == InvalidateType.Mesh)
if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
else if (invalidateType.InvalidateType == InvalidateType.Properties
else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties)
&& invalidateType.Source == this)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
base.OnInvalidate(invalidateType);
else
{
base.OnInvalidate(invalidateType);
}
}
public void SubtractAndReplace()
@ -172,12 +172,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
base.Invalidate(new InvalidateArgs(this, InvalidateType.Content));
});
rebuildLocks.Dispose();
Invalidate(InvalidateType.Children);
return Task.CompletedTask;
});
}

View file

@ -27,22 +27,24 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
/*********************************************************************/
/**************************** OBSOLETE! ******************************/
/************************ USE NEWER VERSION **************************/
/*********************************************************************/
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.PolygonMesh.Processors;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
[Obsolete("Use SubtractObject3D_2 instead", false)]
[ShowUpdateButton]
public class SubtractObject3D : MeshWrapperObject3D, ISelectableChildContainer
{
@ -55,6 +57,53 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
public SelectedChildren SelectedChildren => ItemsToSubtract;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties)
&& invalidateType.Source == this)
{
await Rebuild();
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Subtract(cancellationToken, reporter);
}
catch
{
}
rebuildLocks.Dispose();
Invalidate(InvalidateType.Children);
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
@ -66,13 +115,13 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
bool ItemInSubtractList(IObject3D item)
{
if(ItemsToSubtract.Contains(item.ID))
if (ItemsToSubtract.Contains(item.ID))
{
return true;
}
// check if the wrapped item is in the subtract list
if(item.Children.Count > 0 && ItemsToSubtract.Contains(item.Children.First().ID))
if (item.Children.Count > 0 && ItemsToSubtract.Contains(item.Children.First().ID))
{
return true;
}
@ -128,58 +177,5 @@ namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
}
}
}
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType == InvalidateType.Content
|| invalidateType.InvalidateType == InvalidateType.Matrix
|| invalidateType.InvalidateType == InvalidateType.Mesh)
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
else if (invalidateType.InvalidateType == InvalidateType.Properties
&& invalidateType.Source == this)
{
await Rebuild();
invalidateType = new InvalidateArgs(this, InvalidateType.Content, invalidateType.UndoBuffer);
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Subtract(cancellationToken, reporter);
}
catch
{
}
UiThread.RunOnIdle(() =>
{
rebuildLocks.Dispose();
base.Invalidate(new InvalidateArgs(this, InvalidateType.Content));
});
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,191 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
namespace MatterHackers.MatterControl.PartPreviewWindow.View3D
{
public class SubtractObject3D_2 : OperationSourceContainerObject3D, ISelectableChildContainer
{
public SubtractObject3D_2()
{
Name = "Subtract";
}
public SelectedChildren ItemsToSubtract { get; set; } = new SelectedChildren();
public SelectedChildren SelectedChildren => ItemsToSubtract;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
if ((invalidateType.InvalidateType.HasFlag(InvalidateType.Children)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Matrix)
|| invalidateType.InvalidateType.HasFlag(InvalidateType.Mesh))
&& invalidateType.Source != this
&& !RebuildLocked)
{
await Rebuild();
}
else if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties)
&& invalidateType.Source == this)
{
await Rebuild();
}
base.OnInvalidate(invalidateType);
}
public override Task Rebuild()
{
this.DebugDepth("Rebuild");
var rebuildLocks = this.RebuilLockAll();
// spin up a task to remove holes from the objects in the group
return ApplicationController.Instance.Tasks.Execute(
"Subtract".Localize(),
null,
(reporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
reporter.Report(progressStatus);
try
{
Subtract(cancellationToken, reporter);
}
catch
{
}
rebuildLocks.Dispose();
Invalidate(InvalidateType.Children);
return Task.CompletedTask;
});
}
public void Subtract()
{
Subtract(CancellationToken.None, null);
}
private void Subtract(CancellationToken cancellationToken, IProgress<ProgressStatus> reporter)
{
SourceContainer.Visible = true;
RemoveAllButSource();
var participants = SourceContainer.VisibleMeshes();
if (participants.Count() < 2)
{
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;
}
bool ItemInSubtractList(IObject3D item)
{
if (ItemsToSubtract.Contains(item.ID))
{
return true;
}
// check if the wrapped item is in the subtract list
if (item.Children.Count > 0 && ItemsToSubtract.Contains(item.Children.First().ID))
{
return true;
}
return false;
}
var removeObjects = this.Children
.Where((i) => ItemInSubtractList(i))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
var keepObjects = this.Children
.Where((i) => !ItemInSubtractList(i))
.SelectMany((h) => h.DescendantsAndSelf())
.Where((c) => c.OwnerID == this.ID).ToList();
if (removeObjects.Any()
&& keepObjects.Any())
{
var totalOperations = removeObjects.Count * keepObjects.Count;
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
ProgressStatus progressStatus = new ProgressStatus();
foreach (var remove in removeObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
foreach (var keep in keepObjects.Select((r) => (obj3D: r, matrix: r.WorldMatrix())).ToList())
{
progressStatus.Status = "Copy Remove";
reporter?.Report(progressStatus);
progressStatus.Status = "Copy Keep";
reporter?.Report(progressStatus);
progressStatus.Status = "Do CSG";
reporter?.Report(progressStatus);
var result = BooleanProcessing.Do(keep.obj3D.Mesh, keep.matrix,
remove.obj3D.Mesh, remove.matrix, 1, reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken);
var inverse = keep.matrix.Inverted;
result.Transform(inverse);
using (keep.obj3D.RebuildLock())
{
keep.obj3D.Mesh = result;
}
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
}
remove.obj3D.Visible = false;
SourceContainer.Visible = false;
}
}
}
}
}

View file

@ -136,7 +136,6 @@ namespace MatterHackers.MatterControl
private static ImageBuffer CreateCircularBedGridImage(PrinterConfig printer)
{
Vector3 displayVolumeToBuild = Vector3.ComponentMax(printer.Bed.ViewerVolume, new Vector3(1, 1, 1));
double sizeForMarking = Math.Max(displayVolumeToBuild.X, displayVolumeToBuild.Y);
double cmPerLine = 10;
int skip = 1;
@ -153,6 +152,7 @@ namespace MatterHackers.MatterControl
var bedplateImage = new ImageBuffer(1024, 1024);
Graphics2D graphics2D = bedplateImage.NewGraphics2D();
graphics2D.Clear(bedBaseColor);
var originPixels = new Vector2();
{

View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2019, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Agg.UI;
namespace MatterHackers.MeshVisualizer
{
public class DrawGlContentEventArgs : DrawEventArgs
{
public bool ZBuffered { get; }
public DrawGlContentEventArgs(bool zBuffered, DrawEventArgs e)
: base(e.Graphics2D)
{
ZBuffered = zBuffered;
}
}
}

View file

@ -0,0 +1,208 @@
/*
Copyright (c) 2019, Lars Brubaker
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 MatterHackers.Agg;
using MatterHackers.MatterControl.DesignTools.EditableTypes;
using MatterHackers.RenderOpenGl;
using MatterHackers.RenderOpenGl.OpenGl;
using MatterHackers.VectorMath;
namespace MatterHackers.MeshVisualizer
{
public static class MaterialRendering
{
public static void RenderCylinderOutline(this WorldView world, Matrix4X4 worldMatrix, Vector3 center, double Diameter, double Height, int sides, Color color, double lineWidth = 1, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
for (int i = 0; i < sides; i++)
{
var rotatedPoint = new Vector3(Math.Cos(MathHelper.Tau * i / sides), Math.Sin(MathHelper.Tau * i / sides), 0) * Diameter / 2;
var sideTop = Vector3Ex.Transform(center + rotatedPoint + new Vector3(0, 0, Height / 2), worldMatrix);
var sideBottom = Vector3Ex.Transform(center + rotatedPoint + new Vector3(0, 0, -Height / 2), worldMatrix);
var rotated2Point = new Vector3(Math.Cos(MathHelper.Tau * (i + 1) / sides), Math.Sin(MathHelper.Tau * (i + 1) / sides), 0) * Diameter / 2;
var topStart = sideTop;
var topEnd = Vector3Ex.Transform(center + rotated2Point + new Vector3(0, 0, Height / 2), worldMatrix);
var bottomStart = sideBottom;
var bottomEnd = Vector3Ex.Transform(center + rotated2Point + new Vector3(0, 0, -Height / 2), worldMatrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideTop, ref sideBottom, extendLineLength);
}
world.Render3DLineNoPrep(frustum, sideTop, sideBottom, color, lineWidth);
world.Render3DLineNoPrep(frustum, topStart, topEnd, color, lineWidth);
world.Render3DLineNoPrep(frustum, bottomStart, bottomEnd, color, lineWidth);
}
// turn the lighting back on
GL.Enable(EnableCap.Lighting);
}
public static void RenderAabb(this WorldView world, AxisAlignedBoundingBox bounds, Matrix4X4 matrix, Color color, double width, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
for (int i = 0; i < 4; i++)
{
Vector3 sideStartPosition = Vector3Ex.Transform(bounds.GetBottomCorner(i), matrix);
Vector3 sideEndPosition = Vector3Ex.Transform(bounds.GetTopCorner(i), matrix);
Vector3 bottomStartPosition = sideStartPosition;
Vector3 bottomEndPosition = Vector3Ex.Transform(bounds.GetBottomCorner((i + 1) % 4), matrix);
Vector3 topStartPosition = sideEndPosition;
Vector3 topEndPosition = Vector3Ex.Transform(bounds.GetTopCorner((i + 1) % 4), matrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideStartPosition, ref sideEndPosition, extendLineLength);
GLHelper.ExtendLineEnds(ref topStartPosition, ref topEndPosition, extendLineLength);
GLHelper.ExtendLineEnds(ref bottomStartPosition, ref bottomEndPosition, extendLineLength);
}
// draw each of the edge lines (4) and their touching top and bottom lines (2 each)
world.Render3DLineNoPrep(frustum, sideStartPosition, sideEndPosition, color, width);
world.Render3DLineNoPrep(frustum, topStartPosition, topEndPosition, color, width);
world.Render3DLineNoPrep(frustum, bottomStartPosition, bottomEndPosition, color, width);
}
GL.Enable(EnableCap.Lighting);
}
public static void RenderAxis(this WorldView world, Vector3 position, Matrix4X4 matrix, double size, double lineWidth)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
Vector3 length = Vector3.One * size;
for (int i = 0; i < 3; i++)
{
var min = position;
min[i] -= length[i];
Vector3 start = Vector3Ex.Transform(min, matrix);
var max = position;
max[i] += length[i];
Vector3 end = Vector3Ex.Transform(max, matrix);
var color = Agg.Color.Red;
switch (i)
{
case 1:
color = Agg.Color.Green;
break;
case 2:
color = Agg.Color.Blue;
break;
}
// draw each of the edge lines (4) and their touching top and bottom lines (2 each)
world.Render3DLineNoPrep(frustum, start, end, color, lineWidth);
}
GL.Enable(EnableCap.Lighting);
}
public static void RenderDirectionAxis(this WorldView world, DirectionAxis axis, Matrix4X4 matrix, double size)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
Vector3 length = axis.Normal * size;
var color = Agg.Color.Red;
// draw center line
{
var min = axis.Origin - length;
Vector3 start = Vector3Ex.Transform(min, matrix);
var max = axis.Origin + length;
Vector3 end = Vector3Ex.Transform(max, matrix);
world.Render3DLineNoPrep(frustum, start, end, color, 1);
}
var perpendicular = Vector3.GetPerpendicular(axis.Normal, Vector3.Zero).GetNormal();
// draw some lines to mark the rotation plane
int count = 20;
bool first = true;
var firstEnd = Vector3.Zero;
var lastEnd = Vector3.Zero;
var center = Vector3Ex.Transform(axis.Origin, matrix);
for (int i = 0; i < count; i++)
{
var rotation = size/4 * Vector3Ex.Transform(perpendicular, Matrix4X4.CreateRotation(axis.Normal, MathHelper.Tau * i / count));
// draw center line
var max = axis.Origin + rotation;
Vector3 end = Vector3Ex.Transform(max, matrix);
world.Render3DLineNoPrep(frustum, center, end, color, 1);
if (!first)
{
world.Render3DLineNoPrep(frustum, end, lastEnd, color, 1);
}
else
{
firstEnd = end;
}
lastEnd = end;
first = false;
}
world.Render3DLineNoPrep(frustum, firstEnd, lastEnd, color, 1);
GL.Enable(EnableCap.Lighting);
}
/// <summary>
/// Get the color for a given extruder, falling back to extruder 0 color on -1 (unassigned)
/// </summary>
/// <param name="materialIndex">The extruder/material index to resolve</param>
/// <returns>The color for the given extruder</returns>
public static Color Color(int materialIndex)
{
return ColorF.FromHSL(Math.Max(materialIndex, 0) / 10.0, .99, .49).ToColor();
}
/// <summary>
/// Get the color for a given extruder, falling back to the supplied color on -1 (unassigned)
/// </summary>
/// <param name="materialIndex">The extruder/material index to resolve</param>
/// <param name="unassignedColor">The color to use when the extruder/material has not been assigned</param>
/// <returns>The color for the given extruder</returns>
public static Color Color(int materialIndex, Color unassignedColor)
{
return (materialIndex == -1) ? unassignedColor : ColorF.FromHSL(materialIndex / 10.0, .99, .49).ToColor();
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2014, Lars Brubaker
Copyright (c) 2019, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -40,7 +40,6 @@ using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.EditableTypes;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
@ -52,187 +51,6 @@ using static MatterHackers.Agg.Easing;
namespace MatterHackers.MeshVisualizer
{
public class DrawGlContentEventArgs : DrawEventArgs
{
public bool ZBuffered { get; }
public DrawGlContentEventArgs(bool zBuffered, DrawEventArgs e)
: base(e.Graphics2D)
{
ZBuffered = zBuffered;
}
}
public static class MaterialRendering
{
public static void RenderCylinderOutline(this WorldView world, Matrix4X4 worldMatrix, Vector3 center, double Diameter, double Height, int sides, Color color, double lineWidth = 1, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
for (int i = 0; i < sides; i++)
{
var rotatedPoint = new Vector3(Math.Cos(MathHelper.Tau * i / sides), Math.Sin(MathHelper.Tau * i / sides), 0) * Diameter / 2;
var sideTop = Vector3Ex.Transform(center + rotatedPoint + new Vector3(0, 0, Height / 2), worldMatrix);
var sideBottom = Vector3Ex.Transform(center + rotatedPoint + new Vector3(0, 0, -Height / 2), worldMatrix);
var rotated2Point = new Vector3(Math.Cos(MathHelper.Tau * (i + 1) / sides), Math.Sin(MathHelper.Tau * (i + 1) / sides), 0) * Diameter / 2;
var topStart = sideTop;
var topEnd = Vector3Ex.Transform(center + rotated2Point + new Vector3(0, 0, Height / 2), worldMatrix);
var bottomStart = sideBottom;
var bottomEnd = Vector3Ex.Transform(center + rotated2Point + new Vector3(0, 0, -Height / 2), worldMatrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideTop, ref sideBottom, extendLineLength);
}
world.Render3DLineNoPrep(frustum, sideTop, sideBottom, color, lineWidth);
world.Render3DLineNoPrep(frustum, topStart, topEnd, color, lineWidth);
world.Render3DLineNoPrep(frustum, bottomStart, bottomEnd, color, lineWidth);
}
// turn the lighting back on
GL.Enable(EnableCap.Lighting);
}
public static void RenderAabb(this WorldView world, AxisAlignedBoundingBox bounds, Matrix4X4 matrix, Color color, double width, double extendLineLength = 0)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
for (int i = 0; i < 4; i++)
{
Vector3 sideStartPosition = Vector3Ex.Transform(bounds.GetBottomCorner(i), matrix);
Vector3 sideEndPosition = Vector3Ex.Transform(bounds.GetTopCorner(i), matrix);
Vector3 bottomStartPosition = sideStartPosition;
Vector3 bottomEndPosition = Vector3Ex.Transform(bounds.GetBottomCorner((i + 1) % 4), matrix);
Vector3 topStartPosition = sideEndPosition;
Vector3 topEndPosition = Vector3Ex.Transform(bounds.GetTopCorner((i + 1) % 4), matrix);
if (extendLineLength > 0)
{
GLHelper.ExtendLineEnds(ref sideStartPosition, ref sideEndPosition, extendLineLength);
GLHelper.ExtendLineEnds(ref topStartPosition, ref topEndPosition, extendLineLength);
GLHelper.ExtendLineEnds(ref bottomStartPosition, ref bottomEndPosition, extendLineLength);
}
// draw each of the edge lines (4) and their touching top and bottom lines (2 each)
world.Render3DLineNoPrep(frustum, sideStartPosition, sideEndPosition, color, width);
world.Render3DLineNoPrep(frustum, topStartPosition, topEndPosition, color, width);
world.Render3DLineNoPrep(frustum, bottomStartPosition, bottomEndPosition, color, width);
}
GL.Enable(EnableCap.Lighting);
}
public static void RenderAxis(this WorldView world, Vector3 position, Matrix4X4 matrix, double size, double lineWidth)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
Vector3 length = Vector3.One * size;
for (int i = 0; i < 3; i++)
{
var min = position;
min[i] -= length[i];
Vector3 start = Vector3Ex.Transform(min, matrix);
var max = position;
max[i] += length[i];
Vector3 end = Vector3Ex.Transform(max, matrix);
var color = Agg.Color.Red;
switch (i)
{
case 1:
color = Agg.Color.Green;
break;
case 2:
color = Agg.Color.Blue;
break;
}
// draw each of the edge lines (4) and their touching top and bottom lines (2 each)
world.Render3DLineNoPrep(frustum, start, end, color, lineWidth);
}
GL.Enable(EnableCap.Lighting);
}
public static void RenderDirectionAxis(this WorldView world, DirectionAxis axis, Matrix4X4 matrix, double size)
{
GLHelper.PrepareFor3DLineRender(true);
Frustum frustum = world.GetClippingFrustum();
Vector3 length = axis.Normal * size;
var color = Agg.Color.Red;
// draw center line
{
var min = axis.Origin - length;
Vector3 start = Vector3Ex.Transform(min, matrix);
var max = axis.Origin + length;
Vector3 end = Vector3Ex.Transform(max, matrix);
world.Render3DLineNoPrep(frustum, start, end, color, 1);
}
var perpendicular = Vector3.GetPerpendicular(axis.Normal, Vector3.Zero).GetNormal();
// draw some lines to mark the rotation plane
int count = 20;
bool first = true;
var firstEnd = Vector3.Zero;
var lastEnd = Vector3.Zero;
var center = Vector3Ex.Transform(axis.Origin, matrix);
for (int i = 0; i < count; i++)
{
var rotation = size/4 * Vector3Ex.Transform(perpendicular, Matrix4X4.CreateRotation(axis.Normal, MathHelper.Tau * i / count));
// draw center line
var max = axis.Origin + rotation;
Vector3 end = Vector3Ex.Transform(max, matrix);
world.Render3DLineNoPrep(frustum, center, end, color, 1);
if (!first)
{
world.Render3DLineNoPrep(frustum, end, lastEnd, color, 1);
}
else
{
firstEnd = end;
}
lastEnd = end;
first = false;
}
world.Render3DLineNoPrep(frustum, firstEnd, lastEnd, color, 1);
GL.Enable(EnableCap.Lighting);
}
/// <summary>
/// Get the color for a given extruder, falling back to extruder 0 color on -1 (unassigned)
/// </summary>
/// <param name="materialIndex">The extruder/material index to resolve</param>
/// <returns>The color for the given extruder</returns>
public static Color Color(int materialIndex)
{
return ColorF.FromHSL(Math.Max(materialIndex, 0) / 10.0, .99, .49).ToColor();
}
/// <summary>
/// Get the color for a given extruder, falling back to the supplied color on -1 (unassigned)
/// </summary>
/// <param name="materialIndex">The extruder/material index to resolve</param>
/// <param name="unassignedColor">The color to use when the extruder/material has not been assigned</param>
/// <returns>The color for the given extruder</returns>
public static Color Color(int materialIndex, Color unassignedColor)
{
return (materialIndex == -1) ? unassignedColor : ColorF.FromHSL(materialIndex / 10.0, .99, .49).ToColor();
}
}
public class MeshViewerWidget : GuiWidget
{
private static ImageBuffer ViewOnlyTexture;
@ -442,11 +260,6 @@ namespace MatterHackers.MeshVisualizer
return base.FindDescendants(namesToSearchFor, foundChildren, touchingBounds, seachType, allowInvalidItems);
}
public static Color GetExtruderColor(int extruderIndex)
{
return MaterialRendering.Color(extruderIndex);
}
public void CreateGlDataObject(IObject3D item)
{
if(item.Mesh != null)
@ -763,6 +576,7 @@ namespace MatterHackers.MeshVisualizer
wireColor = lightWireframe;
break;
}
// Draw transparent objects
foreach (var item in transparentMeshes)
{
@ -816,7 +630,6 @@ namespace MatterHackers.MeshVisualizer
{
var bedColor = theme.ResolveColor(Color.White, theme.BackgroundColor.WithAlpha(111));
if (!lookingDownOnBed)
{
bedColor = new Color(bedColor, bedColor.alpha / 4);

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2018, Lars Brubaker, John Lewin
Copyright (c) 2019, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -37,7 +37,6 @@ using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MeshVisualizer;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow
@ -74,13 +73,13 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
allUiFields.Clear();
var column = new FlowLayoutWidget(FlowDirection.TopToBottom)
var printPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Padding = theme.DefaultContainerPadding,
BackgroundColor = menuTheme.BackgroundColor
};
column.AddChild(new TextWidget("Options".Localize(), textColor: menuTheme.TextColor)
printPanel.AddChild(new TextWidget("Options".Localize(), textColor: menuTheme.TextColor)
{
HAnchor = HAnchor.Left
});
@ -94,7 +93,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
MinimumSize = new Vector2(400, 65),
Margin = new BorderDouble(top: 10),
};
column.AddChild(optionsPanel);
printPanel.AddChild(optionsPanel);
foreach (var key in new[] { SettingsKey.layer_height, SettingsKey.fill_density, SettingsKey.create_raft })
{
@ -118,14 +117,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
anySettingOverridden |= printer.Settings.GetValue<bool>(SettingsKey.spiral_vase);
anySettingOverridden |= !string.IsNullOrWhiteSpace(printer.Settings.GetValue(SettingsKey.layer_to_pause));
var sectionWidget = new SectionWidget("Advanced", subPanel, menuTheme, expanded: anySettingOverridden)
var sectionWidget = new SectionWidget("Advanced".Localize(), subPanel, menuTheme, expanded: anySettingOverridden)
{
Name = "Advanced Section",
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Fit,
Margin = 0
};
column.AddChild(sectionWidget);
printPanel.AddChild(sectionWidget);
foreach (var key in new[] { SettingsKey.spiral_vase, SettingsKey.layer_to_pause })
{
@ -154,39 +153,22 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var printerReadyToTakeCommands = printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.FinishedPrint
|| printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.Connected;
var printerIsConnected = printer.Connection.CommunicationState != PrinterCommunication.CommunicationStates.Disconnected;
var printerNeedsToRunSetup = ApplicationController.PrinterNeedsToRunSetup(printer);
// add the start print button
var setupRow = new FlowLayoutWidget()
{
HAnchor = HAnchor.Stretch
};
var printingMessage = "";
// Perform validation before popup
var errors = printer.Validate();
if (!printerIsConnected)
{
printingMessage = "Need to connect before printing".Localize();
}
else if(printerNeedsToRunSetup)
{
printingMessage = "Setup needs to be run before printing".Localize();
}
if (!string.IsNullOrEmpty(printingMessage))
{
setupRow.AddChild(new TextWidget(printingMessage, textColor: menuTheme.TextColor)
{
VAnchor = VAnchor.Center,
AutoExpandBoundsToText = true,
});
}
// Enable print option when no validation Errors exists
var printEnabled = !errors.Any(err => err.ErrorLevel == ValidationErrorLevel.Error);
var startPrintButton = new TextButton("Start Print".Localize(), menuTheme)
{
Name = "Start Print Button",
Enabled = printerIsConnected && !printerNeedsToRunSetup
Enabled = printEnabled
};
startPrintButton.Click += (s, e) =>
@ -212,47 +194,80 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
this.CloseMenu();
};
var hasErrors = errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Error);
var hasErrorsOrWarnings = errors.Any();
if (hasErrorsOrWarnings)
{
string label = hasErrors ? "Action Required".Localize() : "Action Recommended".Localize();
setupRow.AddChild(new TextWidget(label, textColor: (hasErrors ? Color.Red : theme.PrimaryAccentColor), pointSize: theme.DefaultFontSize)
{
VAnchor = VAnchor.Bottom,
AutoExpandBoundsToText = true,
});
}
setupRow.AddChild(new HorizontalSpacer());
setupRow.AddChild(startPrintButton);
column.AddChild(setupRow);
printPanel.AddChild(setupRow);
if (!printerNeedsToRunSetup)
if (printEnabled)
{
theme.ApplyPrimaryActionStyle(startPrintButton);
}
// put in setup if needed
if (printerNeedsToRunSetup && printerIsConnected)
else
{
// add the finish setup button
var finishSetupButton = new TextButton("Setup...".Localize(), theme)
{
Name = "Finish Setup Button",
ToolTipText = "Run setup configuration for printer.".Localize(),
Margin = theme.ButtonSpacing,
Enabled = printerReadyToTakeCommands,
HAnchor = HAnchor.Right,
VAnchor = VAnchor.Absolute,
};
theme.ApplyPrimaryActionStyle(finishSetupButton);
finishSetupButton.Click += (s, e) =>
{
UiThread.RunOnIdle(async () =>
{
await ApplicationController.Instance.PrintPart(
printer.Bed.EditContext,
printer,
null,
CancellationToken.None);
});
this.CloseMenu();
};
column.AddChild(finishSetupButton);
startPrintButton.BackgroundColor = theme.MinimalShade;
}
return column;
if (hasErrorsOrWarnings)
{
var errorsPanel = new ValidationErrorsPanel(errors, menuTheme);
// Conditional layout for right or bottom errors panel alignment
var layoutStyle = FlowDirection.TopToBottom;
//var layoutStyle = FlowDirection.LeftToRight;
if (layoutStyle == FlowDirection.LeftToRight)
{
errorsPanel.HAnchor = HAnchor.Absolute;
errorsPanel.VAnchor = VAnchor.Fit | VAnchor.Top;
errorsPanel.BackgroundColor = theme.ResolveColor(menuTheme.BackgroundColor, theme.PrimaryAccentColor.WithAlpha(30));
errorsPanel.Width = 350;
errorsPanel.Load += (s, e) =>
{
errorsPanel.Parent.BackgroundColor = Color.Transparent;
};
}
else
{
errorsPanel.HAnchor = HAnchor.Stretch;
errorsPanel.VAnchor = VAnchor.Fit;
errorsPanel.Margin = 3;
}
// Instead of the typical case where the print panel is returned, wrap and append validation errors panel
var errorsContainer = new FlowLayoutWidget(layoutStyle)
{
HAnchor = HAnchor.Fit,
VAnchor = VAnchor.Fit,
BackgroundColor = layoutStyle == FlowDirection.TopToBottom ? printPanel.BackgroundColor : Color.Transparent
};
// Clear bottom padding
printPanel.Padding = printPanel.Padding.Clone(bottom: 2);
errorsContainer.AddChild(printPanel);
errorsContainer.AddChild(errorsPanel);
return errorsContainer;
}
return printPanel;
};
this.AddChild(new TextButton("Print".Localize(), theme)

View file

@ -103,7 +103,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
if (doSlicing)
{
var errors = printer.ValidateSettings();
if (errors.Count > 0)
if (errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Error))
{
doSlicing = false;
ApplicationController.Instance.ShowValidationErrors("Slicing Error".Localize(), errors);

View file

@ -0,0 +1,113 @@
/*
Copyright (c) 2019, 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.Collections.Generic;
using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class ValidationErrorsPanel : FlowLayoutWidget
{
public ValidationErrorsPanel(IEnumerable<ValidationError> errors, ThemeConfig theme)
: base (FlowDirection.TopToBottom)
{
this.HAnchor = HAnchor.Absolute;
this.VAnchor = VAnchor.Fit | VAnchor;
this.BackgroundColor = theme.ResolveColor(theme.BackgroundColor, theme.PrimaryAccentColor.WithAlpha(30));
var errorImage = AggContext.StaticData.LoadIcon("SettingsGroupError_16x.png", 16, 16, theme.InvertIcons);
var warningImage = AggContext.StaticData.LoadIcon("SettingsGroupWarning_16x.png", 16, 16, theme.InvertIcons);
var infoImage = AggContext.StaticData.LoadIcon("StatusInfoTip_16x.png", 16, 16);
var fixIcon = AggContext.StaticData.LoadIcon("noun_1306.png", 16, 16, theme.InvertIcons);
foreach (var validationError in errors.OrderByDescending(e => e.ErrorLevel))
{
string errorText, errorDetails;
var settingsValidationError = validationError as SettingsValidationError;
if (settingsValidationError != null)
{
errorText = string.Format(
"{0} {1}",
settingsValidationError.PresentationName,
validationError.ErrorLevel == ValidationErrorLevel.Error ? "Error".Localize() : "Warning".Localize());
errorDetails = validationError.Error;
}
else
{
errorText = validationError.Error;
errorDetails = validationError.Details ?? "";
}
var row = new SettingsRow(errorText, errorDetails, theme, validationError.ErrorLevel == ValidationErrorLevel.Error ? errorImage : warningImage)
{
ArrowDirection = ArrowDirection.Left
};
if (validationError.FixAction is NamedAction action)
{
// Show fix button
var button = new IconButton(fixIcon, theme)
{
ToolTipText = action.Title
};
if (!string.IsNullOrEmpty(action.ID))
{
button.Name = action.ID;
}
button.Click += (s, e) =>
{
action.Action.Invoke();
};
row.AddChild(button);
}
else
{
// Show info indicator hinting that hover will reveal additional details
var button = new IconButton(infoImage, theme)
{
Selectable = false
};
row.AddChild(button);
}
this.AddChild(row);
}
}
}
}

View file

@ -73,7 +73,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var selectedItem = scene.SelectedItem;
if (selectedItem != null)
{
if(selectedItem.CanFlatten)
if (selectedItem.CanFlatten)
{
selectedItem.Flatten(scene.UndoBuffer);
scene.SelectedItem = null;
@ -102,30 +102,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
scene.SelectedItem = null;
progressStatus.Status = "Copy".Localize();
reporter.Report(progressStatus);
var ungroupMesh = selectedItem.Mesh.Copy(cancellationToken, (progress0To1, processingState) =>
{
progressStatus.Progress0To1 = progress0To1 * .2;
progressStatus.Status = processingState;
reporter.Report(progressStatus);
});
if(cancellationToken.IsCancellationRequested)
{
return Task.CompletedTask;
}
progressStatus.Status = "Clean".Localize();
reporter.Report(progressStatus);
if (cancellationToken.IsCancellationRequested)
{
return Task.CompletedTask;
}
using (selectedItem.RebuildLock())
{
selectedItem.Mesh = ungroupMesh;
}
// try to cut it up into multiple meshes
progressStatus.Status = "Split".Localize();
var discreetMeshes = CreateDiscreteMeshes.SplitVolumesIntoMeshes(ungroupMesh, cancellationToken, (double progress0To1, string processingState) =>
var discreetMeshes = CreateDiscreteMeshes.SplitVolumesIntoMeshes(selectedItem.Mesh, cancellationToken, (double progress0To1, string processingState) =>
{
progressStatus.Progress0To1 = .5 + progress0To1 * .5;
progressStatus.Status = processingState;
@ -150,14 +130,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
Mesh = mesh,
}));
foreach(var item in addItems)
foreach (var item in addItems)
{
item.CopyProperties(selectedItem, Object3DPropertyFlags.All);
item.Visible = true;
}
// add and do the undo data
scene.UndoBuffer.AddAndDo(new ReplaceCommand(new List<IObject3D> { selectedItem }, addItems));
scene.UndoBuffer.AddAndDo(new ReplaceCommand(new[] { selectedItem }, addItems));
foreach (var item in addItems)
{
@ -266,7 +246,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// the selection is a group of objects that need to be copied
var copyList = sourceItem.Children.ToList();
scene.SelectedItem = null;
foreach(var item in copyList)
foreach (var item in copyList)
{
var clonedItem = item.Clone();
clonedItem.Translate(xOffset);
@ -348,6 +328,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup)
{
var preLayFlatMatrix = objectToLayFlatGroup.Matrix;
bool firstVertex = true;
IObject3D objectToLayFlat = objectToLayFlatGroup;
@ -398,39 +380,53 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
int faceToLayFlat = -1;
double lowestAngleOfAnyFace = double.MaxValue;
double largestAreaOfAnyFace = 0;
var facesSharingLowestVertex = meshWithLowest.Faces
.Select((face, i) => new { face, i })
.Where(faceAndIndex => meshWithLowest.Vertices[faceAndIndex.face.v0] == sourceVertexPosition
|| meshWithLowest.Vertices[faceAndIndex.face.v1] == sourceVertexPosition
|| meshWithLowest.Vertices[faceAndIndex.face.v2] == sourceVertexPosition)
.Select(j => j.i);
var lowestFacesByAngle = facesSharingLowestVertex.OrderBy(i =>
{
var face = meshWithLowest.Faces[i];
var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix());
return worldNormal.CalculateAngle(-Vector3Float.UnitZ);
});
// Check all the faces that are connected to the lowest point to find out which one to lay flat.
for (int faceIndex=0; faceIndex<meshWithLowest.Faces.Count; faceIndex++)
foreach (var faceIndex in lowestFacesByAngle)
{
var face = meshWithLowest.Faces[faceIndex];
if (meshWithLowest.Vertices[face.v0] != sourceVertexPosition
&& meshWithLowest.Vertices[face.v1] != sourceVertexPosition
&& meshWithLowest.Vertices[face.v2] != sourceVertexPosition)
{
// this face does not contain the vertex
continue;
}
double biggestAngleToFaceVertex = double.MinValue;
var faceIndices = new int[] { face.v0, face.v1, face.v2 };
foreach (var vi in faceIndices)
var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix());
var worldAngleDegrees = MathHelper.RadiansToDegrees(worldNormal.CalculateAngle(-Vector3Float.UnitZ));
double largestAreaFound = 0;
var faceVeretexIndices = new int[] { face.v0, face.v1, face.v2 };
foreach (var vi in faceVeretexIndices)
{
if (meshWithLowest.Vertices[vi] != lowestPosition)
{
var faceVertexPosition = meshWithLowest.Vertices[vi].Transform(itemToLayFlat.WorldMatrix());
var pointRelLowest = faceVertexPosition - lowestPosition;
double xLeg = new Vector2(pointRelLowest.X, pointRelLowest.Y).Length;
double yLeg = pointRelLowest.Z;
double angle = Math.Atan2(yLeg, xLeg);
if (angle > biggestAngleToFaceVertex)
var planSurfaceArea = 0.0;
foreach (var coPlanarFace in meshWithLowest.GetCoplanerFaces(faceIndex))
{
biggestAngleToFaceVertex = angle;
planSurfaceArea += meshWithLowest.GetSurfaceArea(coPlanarFace);
}
if (largestAreaOfAnyFace == 0
|| (planSurfaceArea > largestAreaFound
&& worldAngleDegrees < 45))
{
largestAreaFound = planSurfaceArea;
}
}
}
if (biggestAngleToFaceVertex < lowestAngleOfAnyFace)
if (largestAreaFound > largestAreaOfAnyFace)
{
lowestAngleOfAnyFace = biggestAngleToFaceVertex;
largestAreaOfAnyFace = largestAreaFound;
faceToLayFlat = faceIndex;
}
}
@ -461,6 +457,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
PlatingHelper.PlaceOnBed(objectToLayFlatGroup);
scene.UndoBuffer.Add(new TransformCommand(objectToLayFlatGroup, preLayFlatMatrix, objectToLayFlatGroup.Matrix));
}
internal class ArrangeUndoCommand : IUndoRedoCommand

View file

@ -36,66 +36,58 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class DeleteCommand : IUndoRedoCommand
{
private List<IObject3D> items = new List<IObject3D>();
private List<(IObject3D parent, IObject3D item)> items = new List<(IObject3D parent, IObject3D item)>();
private InteractiveScene scene;
public DeleteCommand(InteractiveScene scene, IObject3D deletingItem)
{
this.scene = scene;
if (deletingItem is SelectionGroupObject3D)
{
var childrenToAdd = deletingItem.Children;
// push whatever happened to the selection into the objects before saving them
scene.ClearSelection();
// save them in our list
foreach (var item in childrenToAdd)
{
items.Add(item);
}
SetDeletionObjects(scene, deletingItem.Children);
}
else
{
this.items.Add(deletingItem);
SetDeletionObjects(scene, new IObject3D[] { deletingItem });
}
}
public DeleteCommand(InteractiveScene scene, IEnumerable<IObject3D> deletingItems)
{
SetDeletionObjects(scene, deletingItems);
}
private void SetDeletionObjects(InteractiveScene scene, IEnumerable<IObject3D> deletingItems)
{
this.scene = scene;
scene.ClearSelection();
// save them in our list
foreach (var item in deletingItems)
{
items.Add((item.Parent, item));
}
}
public void Do()
{
scene.ClearSelection();
scene.Children.Modify(list =>
using (new SelectionMaintainer(scene))
{
foreach (var item in items)
foreach(var item in items)
{
list.Remove(item);
item.parent.Children.Remove(item.item);
}
});
scene.SelectLastChild();
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
}
}
public void Undo()
{
scene.Children.Modify(list =>
using (new SelectionMaintainer(scene))
{
foreach (var item in items)
{
list.Add(item);
item.parent.Children.Add(item.item);
}
});
scene.ClearSelection();
foreach (var item in items)
{
scene.AddToSelection(item);
}
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
}
}
}

View file

@ -38,18 +38,20 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
private IEnumerable<IObject3D> items;
private InteractiveScene scene;
private bool selectAfterInsert;
bool firstPass = true;
public InsertCommand(InteractiveScene scene, IObject3D insertingItem)
: this(scene, new IObject3D[] { insertingItem })
public InsertCommand(InteractiveScene scene, IObject3D insertingItem, bool selectAfterInsert = true)
: this(scene, new IObject3D[] { insertingItem }, selectAfterInsert)
{
}
public InsertCommand(InteractiveScene scene, IEnumerable<IObject3D> insertingItem)
public InsertCommand(InteractiveScene scene, IEnumerable<IObject3D> insertingItem, bool selectAfterInsert = true)
{
this.scene = scene;
this.items = insertingItem;
this.selectAfterInsert = selectAfterInsert;
}
public void Do()
@ -62,12 +64,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
scene.Children.Modify(list => list.AddRange(items));
scene.SelectedItem = null;
foreach(var item in items)
if (selectAfterInsert)
{
scene.AddToSelection(item);
foreach (var item in items)
{
scene.AddToSelection(item);
}
}
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
}
public void Undo()
@ -86,7 +91,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
scene.SelectedItem = null;
}
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
}
}
}

View file

@ -68,7 +68,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
});
scene.SelectLastChild();
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
}
public void Undo()
@ -93,7 +93,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
});
scene.SelectLastChild();
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content));
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
}
}
}

View file

@ -238,8 +238,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
SplitterSize = theme.SplitterWidth,
SplitterBackground = theme.SplitterBackground
};
historyAndProperties.Panel1.MinimumSize = new Vector2(0, 120);
historyAndProperties.Panel2.MinimumSize = new Vector2(0, 120);
historyAndProperties.Panel1.MinimumSize = new Vector2(0, 60);
historyAndProperties.Panel2.MinimumSize = new Vector2(0, 60);
modelViewSidePanel.AddChild(historyAndProperties);
@ -382,6 +382,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private void RebuildTree()
{
rebuildTreePending = false;
workspaceName.Text = sceneContext.Scene.Name ?? "";
// Top level selection only - rebuild tree
@ -391,7 +392,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
foreach (var child in sceneContext.Scene.Children)
{
if (child.GetType().IsDefined(typeof(HideFromTreeViewAttribute), false))
if (child is GeneratedSupportObject3D)
{
continue;
}
@ -416,202 +417,141 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
bool invertIcons = ApplicationController.Instance.MenuTheme.InvertIcons;
return new Dictionary<string, NamedAction>()
// Build workspace actions, each having a unique ID
var actions = new []
{
new NamedAction()
{
"Insert",
new NamedAction()
ID = "Print",
Title = "Print".Localize(),
Shortcut = "Ctrl+P",
Action = this.PushToPrinterAndPrint,
IsEnabled = () => sceneContext.EditableScene
|| (sceneContext.EditContext.SourceItem is ILibraryAsset libraryAsset
&& string.Equals(Path.GetExtension(libraryAsset.FileName) ,".gcode" ,StringComparison.OrdinalIgnoreCase))
},
new NamedAction()
{
ID = "Cut",
Title = "Cut".Localize(),
Shortcut = "Ctrl+X",
Action = () =>
{
ID = "Insert",
Title = "Insert".Localize(),
Icon = AggContext.StaticData.LoadIcon("cube.png", 16, 16, invertIcons),
Action = () =>
{
var extensionsWithoutPeriod = new HashSet<string>(ApplicationSettings.OpenDesignFileParams.Split('|').First().Split(',').Select(s => s.Trim().Trim('.')));
foreach(var extension in ApplicationController.Instance.Library.ContentProviders.Keys)
{
extensionsWithoutPeriod.Add(extension.ToUpper());
}
var extensionsArray = extensionsWithoutPeriod.OrderBy(t => t).ToArray();
string filter = string.Format(
"{0}|{1}",
string.Join(",", extensionsArray),
string.Join("", extensionsArray.Select(e => $"*.{e.ToLower()};").ToArray()));
UiThread.RunOnIdle(() =>
{
AggContext.FileDialogs.OpenFileDialog(
new OpenFileDialogParams(filter, multiSelect: true),
(openParams) =>
sceneContext.Scene.Cut();
},
IsEnabled = () => sceneContext.Scene.SelectedItem != null
},
new NamedAction()
{
ID = "Copy",
Title = "Copy".Localize(),
Shortcut = "Ctrl+C",
Action = () =>
{
sceneContext.Scene.Copy();
},
IsEnabled = () => sceneContext.Scene.SelectedItem != null
},
new NamedAction()
{
ID = "Paste",
Title = "Paste".Localize(),
Shortcut = "Ctrl+V",
Action = () =>
{
sceneContext.Paste();
},
IsEnabled = () => Clipboard.Instance.ContainsImage || Clipboard.Instance.GetText() == "!--IObjectSelection--!"
},
new NamedAction()
{
ID = "Delete",
Icon = AggContext.StaticData.LoadIcon("remove.png").SetPreMultiply(),
Title = "Remove".Localize(),
Action = sceneContext.Scene.DeleteSelection,
IsEnabled = () => sceneContext.Scene.SelectedItem != null
},
new NamedAction()
{
ID = "Export",
Title = "Export".Localize(),
Icon = AggContext.StaticData.LoadIcon("cube_export.png", 16, 16, invertIcons),
Action = () =>
{
ApplicationController.Instance.ExportLibraryItems(
new[] { new InMemoryLibraryItem(sceneContext.Scene)},
centerOnBed: false,
printer: printer);
},
IsEnabled = () => sceneContext.EditableScene
|| (sceneContext.EditContext.SourceItem is ILibraryAsset libraryAsset
&& string.Equals(Path.GetExtension(libraryAsset.FileName) ,".gcode" ,StringComparison.OrdinalIgnoreCase))
},
new NamedAction()
{
ID = "Save",
Title = "Save".Localize(),
Shortcut = "Ctrl+S",
Action = () =>
{
ApplicationController.Instance.Tasks.Execute("Saving".Localize(), printer, sceneContext.SaveChanges).ConfigureAwait(false);
},
IsEnabled = () => sceneContext.EditableScene
},
new NamedAction()
{
ID = "SaveAs",
Title = "Save As".Localize(),
Action = () => UiThread.RunOnIdle(() =>
{
DialogWindow.Show(
new SaveAsPage(
async (newName, destinationContainer) =>
{
// Save to the destination provider
if (destinationContainer is ILibraryWritableContainer writableContainer)
{
ViewControls3D.LoadAndAddPartsToPlate(this, openParams.FileNames, sceneContext);
});
});
}
}
},
{
"Print",
new NamedAction()
{
ID = "Print",
Title = "Print".Localize(),
Shortcut = "Ctrl+P",
Action = this.PushToPrinterAndPrint,
IsEnabled = () => sceneContext.EditableScene
|| (sceneContext.EditContext.SourceItem is ILibraryAsset libraryAsset
&& string.Equals(Path.GetExtension(libraryAsset.FileName) ,".gcode" ,StringComparison.OrdinalIgnoreCase))
}
},
{
"Cut",
new NamedAction()
{
ID = "Cut",
Title = "Cut".Localize(),
Shortcut = "Ctrl+X",
Action = () =>
{
sceneContext.Scene.Cut();
},
IsEnabled = () => sceneContext.Scene.SelectedItem != null
}
},
{
"Copy",
new NamedAction()
{
ID = "Copy",
Title = "Copy".Localize(),
Shortcut = "Ctrl+C",
Action = () =>
{
sceneContext.Scene.Copy();
},
IsEnabled = () => sceneContext.Scene.SelectedItem != null
}
},
{
"Paste",
new NamedAction()
{
ID = "Paste",
Title = "Paste".Localize(),
Shortcut = "Ctrl+V",
Action = () =>
{
sceneContext.Paste();
},
IsEnabled = () => Clipboard.Instance.ContainsImage || Clipboard.Instance.GetText() == "!--IObjectSelection--!"
}
},
{
"Delete",
new NamedAction()
{
ID = "Delete",
Icon = AggContext.StaticData.LoadIcon("remove.png").SetPreMultiply(),
Title = "Remove".Localize(),
Action = sceneContext.Scene.DeleteSelection,
IsEnabled = () => sceneContext.Scene.SelectedItem != null
}
},
{
"Export",
new NamedAction()
{
ID = "Export",
Title = "Export".Localize(),
Icon = AggContext.StaticData.LoadIcon("cube_export.png", 16, 16, invertIcons),
Action = () =>
{
ApplicationController.Instance.ExportLibraryItems(
new[] { new InMemoryLibraryItem(sceneContext.Scene)},
centerOnBed: false,
printer: printer);
},
IsEnabled = () => sceneContext.EditableScene
|| (sceneContext.EditContext.SourceItem is ILibraryAsset libraryAsset
&& string.Equals(Path.GetExtension(libraryAsset.FileName) ,".gcode" ,StringComparison.OrdinalIgnoreCase))
}
},
{
"Save",
new NamedAction()
{
ID = "Save",
Title = "Save".Localize(),
Shortcut = "Ctrl+S",
Action = () =>
{
ApplicationController.Instance.Tasks.Execute("Saving".Localize(), printer, sceneContext.SaveChanges).ConfigureAwait(false);
},
IsEnabled = () => sceneContext.EditableScene
}
},
{
"SaveAs",
new NamedAction()
{
ID = "SaveAs",
Title = "Save As".Localize(),
Action = () => UiThread.RunOnIdle(() =>
{
DialogWindow.Show(
new SaveAsPage(
async (newName, destinationContainer) =>
{
// Save to the destination provider
if (destinationContainer is ILibraryWritableContainer writableContainer)
// Wrap stream with ReadOnlyStream library item and add to container
writableContainer.Add(new[]
{
// Wrap stream with ReadOnlyStream library item and add to container
writableContainer.Add(new[]
new InMemoryLibraryItem(sceneContext.Scene)
{
new InMemoryLibraryItem(sceneContext.Scene)
{
Name = newName
}
});
Name = newName
}
});
destinationContainer.Dispose();
}
}));
}),
IsEnabled = () => sceneContext.EditableScene
}
destinationContainer.Dispose();
}
}));
}),
IsEnabled = () => sceneContext.EditableScene
},
new NamedAction()
{
"ArrangeAll",
new NamedAction()
ID = "ArrangeAll",
Title = "Arrange All Parts".Localize(),
Action = () =>
{
ID = "ArrangeAll",
Title = "Arrange All Parts".Localize(),
Action = () =>
{
sceneContext.Scene.AutoArrangeChildren(this.BedCenter);
},
IsEnabled = () => sceneContext.EditableScene
}
sceneContext.Scene.AutoArrangeChildren(this.BedCenter);
},
IsEnabled = () => sceneContext.EditableScene
},
new NamedAction()
{
"ClearBed",
new NamedAction()
ID = "ClearBed",
Title = "Clear Bed".Localize(),
Action = () =>
{
ID = "ClearBed",
Title = "Clear Bed".Localize(),
Action = () =>
UiThread.RunOnIdle(() =>
{
UiThread.RunOnIdle(() =>
{
this.ClearPlate();
});
}
this.ClearPlate();
});
}
}
};
// Construct dictionary from workspace actions by ID
return actions.ToDictionary(a => a.ID);
}
private void ViewState_ViewModeChanged(object sender, ViewModeChangedEventArgs e)
@ -1068,7 +1008,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
this.deferEditorTillMouseUp = false;
Scene_SelectionChanged(null, null);
Scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content, null));
Scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
// Set focus to View3DWidget after drag-drop
UiThread.RunOnIdle(this.Focus);
@ -1881,18 +1821,26 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
// TODO: Consider if we should always allow DragDrop or if we should prevent during printer or other scenarios
private bool AllowDragDrop() => true;
private bool rebuildTreePending = false;
private void Scene_Invalidated(object sender, InvalidateArgs e)
{
if (e.InvalidateType == InvalidateType.Content)
if (e.InvalidateType.HasFlag(InvalidateType.Children)
&& !rebuildTreePending)
{
rebuildTreePending = true;
UiThread.RunOnIdle(this.RebuildTree);
}
if (e.InvalidateType == InvalidateType.Name)
if (e.InvalidateType.HasFlag(InvalidateType.Name))
{
// clear and restore the selection so we have the name change
var lastSelectedItem = Scene.SelectedItem;
UiThread.RunOnIdle(this.RebuildTree);
if (!rebuildTreePending)
{
rebuildTreePending = true;
UiThread.RunOnIdle(this.RebuildTree);
}
Scene.SelectedItem = null;
Scene.SelectedItem = lastSelectedItem;
}

View file

@ -325,7 +325,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
undoButton = new IconButton(AggContext.StaticData.LoadIcon("Undo_grey_16x.png", 16, 16, theme.InvertIcons), theme)
{
Name = "3D View Undo",
ToolTipText = "Undo",
ToolTipText = "Undo".Localize(),
Enabled = false,
Margin = theme.ButtonSpacing,
VAnchor = VAnchor.Center
@ -341,7 +341,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
Name = "3D View Redo",
Margin = theme.ButtonSpacing,
ToolTipText = "Redo",
ToolTipText = "Redo".Localize(),
Enabled = false,
VAnchor = VAnchor.Center
};
@ -527,7 +527,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
new OpenFileDialogParams(filter, multiSelect: true),
(openParams) =>
{
ViewControls3D.LoadAndAddPartsToPlate(this, openParams.FileNames, ApplicationController.Instance.DragDropData.SceneContext);
sceneContext.AddToPlate(openParams.FileNames);
});
}, .1);
};
@ -791,117 +791,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
return saveButton;
}
public static async void LoadAndAddPartsToPlate(GuiWidget originatingWidget, string[] filesToLoad, BedConfig sceneContext)
{
if (filesToLoad != null && filesToLoad.Length > 0)
{
await Task.Run(() => loadAndAddPartsToPlate(filesToLoad, sceneContext));
if (originatingWidget.HasBeenClosed)
{
return;
}
var scene = sceneContext.Scene;
bool addingOnlyOneItem = scene.Children.Count == scene.Children.Count + 1;
if (scene.HasChildren())
{
if (addingOnlyOneItem)
{
// if we are only adding one part to the plate set the selection to it
scene.SelectLastChild();
}
}
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Content, null));
}
}
private static async Task loadAndAddPartsToPlate(string[] filesToLoadIncludingZips, BedConfig sceneContext)
{
if (filesToLoadIncludingZips?.Any() == true)
{
var scene = sceneContext.Scene;
// When a single GCode file is selected, swap the plate to the new GCode content
if (filesToLoadIncludingZips.Count() == 1
&& filesToLoadIncludingZips.FirstOrDefault() is string firstFilePath
&& Path.GetExtension(firstFilePath).ToUpper() == ".GCODE")
{
// Special case for GCode which changes loaded scene to special mode for GCode
await sceneContext.LoadContent(
new EditContext()
{
SourceItem = new FileSystemFileItem(firstFilePath),
ContentStore = null // No content store for GCode, otherwise PlatingHistory
});
return;
}
var filesToLoad = new List<string>();
foreach (string loadedFileName in filesToLoadIncludingZips)
{
string extension = Path.GetExtension(loadedFileName).ToUpper();
if ((extension != ""
&& extension != ".ZIP"
&& extension != ".GCODE"
&& ApplicationController.Instance.Library.IsContentFileType(loadedFileName))
)
{
filesToLoad.Add(loadedFileName);
}
else if (extension == ".ZIP")
{
List<PrintItem> partFiles = ProjectFileHandler.ImportFromProjectArchive(loadedFileName);
if (partFiles != null)
{
foreach (PrintItem part in partFiles)
{
string itemExtension = Path.GetExtension(part.FileLocation).ToUpper();
if (itemExtension != ".GCODE")
{
filesToLoad.Add(part.FileLocation);
}
}
}
}
}
var itemCache = new Dictionary<string, IObject3D>();
foreach (string filePath in filesToLoad)
{
var libraryItem = new FileSystemFileItem(filePath);
IObject3D object3D = null;
await ApplicationController.Instance.Tasks.Execute("Loading".Localize() + " " + Path.GetFileName(filePath), sceneContext.Printer, async (progressReporter, cancellationToken) =>
{
var progressStatus = new ProgressStatus();
progressReporter.Report(progressStatus);
object3D = await libraryItem.CreateContent((double progress0To1, string processingState) =>
{
progressStatus.Progress0To1 = progress0To1;
progressStatus.Status = processingState;
progressReporter.Report(progressStatus);
});
});
if (object3D != null)
{
scene.Children.Modify(list => list.Add(object3D));
PlatingHelper.MoveToOpenPositionRelativeGroup(object3D, scene.Children);
}
}
}
}
public override void OnClosed(EventArgs e)
{
// Unregister listeners