Merge branch '2.19.2'
This commit is contained in:
commit
6731fdc5d8
168 changed files with 13231 additions and 7124 deletions
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
208
MatterControlLib/PartPreviewWindow/View3D/MaterialRendering.cs
Normal file
208
MatterControlLib/PartPreviewWindow/View3D/MaterialRendering.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue