Support has first release quality

Made 'support type' work selectable
This commit is contained in:
Lars Brubaker 2019-01-23 16:08:27 -08:00
parent 08b4f4b5ed
commit 5f0cacf4c7
4 changed files with 269 additions and 243 deletions

View file

@ -308,18 +308,27 @@ namespace MatterHackers.MatterControl.DesignTools
}
//field.Content
undoBuffer.AddAndDo(new UndoRedoActions(() =>
if (undoBuffer != null)
{
property.SetValue(valueFromString(oldValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
},
() =>
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
property.SetValue(valueFromString(oldValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
},
() =>
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}));
}
else
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}));
}
};
}

View file

@ -34,8 +34,6 @@ using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Transform;
using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.PolygonMesh;
using MatterHackers.RayTracer;
@ -88,6 +86,8 @@ namespace MatterHackers.MatterControl.DesignTools
this.scene = scene;
}
public enum SupportGenerationType { Normal, From_Bed }
public double MaxOverHangAngle
{
get
@ -133,30 +133,30 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
public SupportGenerationType SupportType
{
get
{
var supportString = UserSettings.Instance.get(UserSettingsKey.SupportGenerationType);
if (Enum.TryParse(supportString, out SupportGenerationType supportType))
{
return supportType;
}
return SupportGenerationType.Normal;
}
set
{
UserSettings.Instance.set(UserSettingsKey.SupportGenerationType, value.ToString());
}
}
/// <summary>
/// The amount to reduce the pillars so they are separated in the 3D view
/// </summary>
private double reduceAmount => .99;
// function to get all the columns that need support generation
IEnumerable<(int x, int y)> GetSupportCorrodinates(ImageBuffer supportNeededImage)
{
var buffer = supportNeededImage.GetBuffer();
// check if the image has any alpha set to something other than 255
for (int y = 0; y < supportNeededImage.Height; y++)
{
var yOffset = supportNeededImage.GetBufferOffsetY(y);
for (int x = 0; x < supportNeededImage.Width; x++)
{
// get the alpha at this pixel
//if (buffer[yOffset + x] > 0)
{
yield return (x, y);
}
}
}
}
public Task Create(IProgress<ProgressStatus> progress, CancellationToken cancelationToken)
{
ProgressStatus status = new ProgressStatus();
@ -177,8 +177,7 @@ namespace MatterHackers.MatterControl.DesignTools
AxisAlignedBoundingBox allBounds = AxisAlignedBoundingBox.Empty();
foreach (var candidate in supportCandidates)
{
var matrix = candidate.WorldMatrix(scene);
allBounds += candidate.GetAxisAlignedBoundingBox();
allBounds += candidate.GetAxisAlignedBoundingBox(candidate.Matrix.Inverted * candidate.WorldMatrix());
}
// create the gird of possible support
@ -223,204 +222,6 @@ namespace MatterHackers.MatterControl.DesignTools
return Task.CompletedTask;
}
int GetNextTop(int i, List<(double z, bool bottom)> planes)
{
while (i < planes.Count
&& planes[i].bottom)
{
i++;
}
return i;
}
int GetNextBottom(int i, List<(double z, bool bottom)> planes)
{
// first skip all the tops
while (i < planes.Count
&& !planes[i].bottom)
{
i++;
}
// then look for the last bottom before next top
while (i < planes.Count
&& planes[i].bottom
&& planes.Count > i + 1
&& planes[i + 1].bottom)
{
i++;
}
return i;
}
private void AddSupportColumns(RectangleDouble gridBounds, Dictionary<(int x, int y), List<(double z, bool bottom)>> detectedPlanes)
{
IObject3D supportColumnsToAdd = new Object3D();
bool fromBed = false;
foreach (var kvp in detectedPlanes)
{
if(kvp.Value.Count == 0)
{
continue;
}
int i = 0;
kvp.Value.Sort((a, b) =>
{
return a.z.CompareTo(b.z);
});
var yPos = (gridBounds.Bottom + kvp.Key.y) * PillarSize;
var xPos = (gridBounds.Left + kvp.Key.x) * PillarSize;
if (fromBed)
{
i = GetNextBottom(i, kvp.Value);
if (kvp.Value[i].bottom)
{
AddSupportColumn(supportColumnsToAdd, xPos, yPos, 0, kvp.Value[i].z);
}
}
else
{
double lastTopZ = 0;
int lastBottom = i;
do
{
// if the first plane is a top, move to the last top before we find a bottom
if(i == 0
&& !kvp.Value[i].bottom)
{
i = GetNextTop(i + 1, kvp.Value);
if (i < kvp.Value.Count)
{
lastTopZ = kvp.Value[i].z;
}
}
lastBottom = i;
// find all open arreas in the list and add support
i = GetNextBottom(i, kvp.Value);
if (i < kvp.Value.Count
&& kvp.Value[i].bottom)
{
AddSupportColumn(supportColumnsToAdd, xPos, yPos, lastTopZ, kvp.Value[i].z);
}
i = GetNextTop(i+1, kvp.Value);
if (i < kvp.Value.Count)
{
lastTopZ = kvp.Value[i].z;
}
} while (i != lastBottom && i < kvp.Value.Count);
}
}
scene.Children.Modify(list =>
{
list.AddRange(supportColumnsToAdd.Children);
});
}
private Dictionary<(int x, int y), List<(double z, bool bottom)>> DetectRequiredSupportByTracing(RectangleDouble gridBounds, IEnumerable<IObject3D> supportCandidates)
{
var traceData = GetTraceData(supportCandidates);
// keep a list of all the detected planes in each support column
var detectedPlanes = new Dictionary<(int x, int y), List<(double z, bool bottom)>>();
int gridWidth = (int)gridBounds.Width;
int gridHeight = (int)gridBounds.Height;
// 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 < gridHeight; y++)
{
for (int x = 0; x < gridWidth; x++)
{
IntersectInfo upHit = null;
for (double yOffset = -1; yOffset <= 1; yOffset++)
{
for (double xOffset = -1; xOffset <= 1; xOffset++)
{
var yPos = (gridBounds.Bottom + y) * PillarSize + (yOffset * PillarSize / 2);
var xPos = (gridBounds.Left + x) * PillarSize + (xOffset * PillarSize / 2);
var upRay = new Ray(new Vector3(xPos + .000013, yPos - .00027, 0), Vector3.UnitZ, intersectionType: IntersectionType.Both);
do
{
upHit = traceData.GetClosestIntersection(upRay);
if (upHit != null)
{
if (!detectedPlanes.ContainsKey((x, y)))
{
detectedPlanes.Add((x, y), new List<(double z, bool bottom)>());
}
detectedPlanes[(x, y)].Add((upHit.HitPosition.Z, upHit.normalAtHit.Z < 0));
// 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);
}
}
}
}
return detectedPlanes;
}
private IPrimitive GetTraceData(IEnumerable<IObject3D> supportCandidates)
{
List<Vector3Float> supportVerts;
FaceList supportFaces;
// find all the faces that are candidates for support
supportVerts = new List<Vector3Float>();
supportFaces = new FaceList();
foreach (var item in supportCandidates)
{
// add all the down faces to supportNeededImage
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)));
// check if the face is pointing in the up direction at all
bool isUpFace = angle > 90;
// check if the face is pointing down
if (angle < MaxOverHangAngle
|| isUpFace)
{
var face = item.Mesh.Faces[faceIndex];
var verts = new int[] { face.v0, face.v1, face.v2 };
var p0 = item.Mesh.Vertices[face.v0].Transform(matrix);
var p1 = item.Mesh.Vertices[face.v1].Transform(matrix);
var p2 = item.Mesh.Vertices[face.v2].Transform(matrix);
var vc = supportVerts.Count;
supportVerts.Add(p0);
supportVerts.Add(p1);
supportVerts.Add(p2);
supportFaces.Add(vc, vc + 1, vc + 2, face0Normal);
}
}
// add all mesh edges that need support
// add all unsupported vertices (low points of a face group that individually do not need support)
}
return supportFaces.CreateTraceData(supportVerts);
}
public Agg.UI.HAnchor SupportType { get; set; } = Agg.UI.HAnchor.Fit;
public void RemoveExisting()
{
var existingSupports = scene.Children.Where(i => i.GetType() == typeof(GeneratedSupportObject3D));
@ -493,5 +294,222 @@ namespace MatterHackers.MatterControl.DesignTools
holder.Children.Add(support);
}
private void AddSupportColumns(RectangleDouble gridBounds, Dictionary<(int x, int y), List<(double z, bool bottom)>> detectedPlanes)
{
IObject3D supportColumnsToAdd = new Object3D();
bool fromBed = SupportType == SupportGenerationType.From_Bed;
var halfPillar = PillarSize / 2;
foreach (var kvp in detectedPlanes)
{
if (kvp.Value.Count == 0)
{
continue;
}
int i = 0;
kvp.Value.Sort((a, b) =>
{
return a.z.CompareTo(b.z);
});
var yPos = (gridBounds.Bottom + kvp.Key.y) * PillarSize + halfPillar;
var xPos = (gridBounds.Left + kvp.Key.x) * PillarSize + halfPillar;
if (fromBed)
{
i = GetNextBottom(i, kvp.Value);
if (kvp.Value[i].bottom)
{
AddSupportColumn(supportColumnsToAdd, xPos, yPos, 0, kvp.Value[i].z);
}
}
else
{
double lastTopZ = 0;
int lastBottom = i;
do
{
// if the first plane is a top, move to the last top before we find a bottom
if (i == 0
&& !kvp.Value[i].bottom)
{
i = GetNextTop(i + 1, kvp.Value);
if (i < kvp.Value.Count)
{
lastTopZ = kvp.Value[i].z;
}
}
lastBottom = i;
// find all open arreas in the list and add support
i = GetNextBottom(i, kvp.Value);
if (i < kvp.Value.Count
&& kvp.Value[i].bottom)
{
AddSupportColumn(supportColumnsToAdd, xPos, yPos, lastTopZ, kvp.Value[i].z);
}
i = GetNextTop(i + 1, kvp.Value);
if (i < kvp.Value.Count)
{
lastTopZ = kvp.Value[i].z;
}
} while (i != lastBottom && i < kvp.Value.Count);
}
}
scene.Children.Modify(list =>
{
list.AddRange(supportColumnsToAdd.Children);
});
}
private Dictionary<(int x, int y), List<(double z, bool bottom)>> DetectRequiredSupportByTracing(RectangleDouble gridBounds, IEnumerable<IObject3D> supportCandidates)
{
var traceData = GetTraceData(supportCandidates);
// keep a list of all the detected planes in each support column
var detectedPlanes = new Dictionary<(int x, int y), List<(double z, bool bottom)>>();
int gridWidth = (int)gridBounds.Width;
int gridHeight = (int)gridBounds.Height;
// 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 < gridHeight; y++)
{
for (int x = 0; x < gridWidth; x++)
{
IntersectInfo upHit = null;
for (double yOffset = -1; yOffset <= 1; yOffset++)
{
for (double xOffset = -1; xOffset <= 1; xOffset++)
{
var halfPillar = PillarSize / 2;
var yPos = (gridBounds.Bottom + y) * PillarSize + halfPillar + (yOffset * halfPillar);
var xPos = (gridBounds.Left + x) * PillarSize + halfPillar + (xOffset * halfPillar);
var upRay = new Ray(new Vector3(xPos + .000013, yPos - .00027, 0), Vector3.UnitZ, intersectionType: IntersectionType.Both);
do
{
upHit = traceData.GetClosestIntersection(upRay);
if (upHit != null)
{
if (!detectedPlanes.ContainsKey((x, y)))
{
detectedPlanes.Add((x, y), new List<(double z, bool bottom)>());
}
detectedPlanes[(x, y)].Add((upHit.HitPosition.Z, upHit.normalAtHit.Z < 0));
// 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);
}
}
}
}
return detectedPlanes;
}
private int GetNextBottom(int i, List<(double z, bool bottom)> planes)
{
// first skip all the tops
while (i < planes.Count
&& !planes[i].bottom)
{
i++;
}
// then look for the last bottom before next top
while (i < planes.Count
&& planes[i].bottom
&& planes.Count > i + 1
&& planes[i + 1].bottom)
{
i++;
}
return i;
}
private int GetNextTop(int i, List<(double z, bool bottom)> planes)
{
while (i < planes.Count
&& planes[i].bottom)
{
i++;
}
return i;
}
// function to get all the columns that need support generation
private IEnumerable<(int x, int y)> GetSupportCorrodinates(ImageBuffer supportNeededImage)
{
var buffer = supportNeededImage.GetBuffer();
// check if the image has any alpha set to something other than 255
for (int y = 0; y < supportNeededImage.Height; y++)
{
var yOffset = supportNeededImage.GetBufferOffsetY(y);
for (int x = 0; x < supportNeededImage.Width; x++)
{
// get the alpha at this pixel
//if (buffer[yOffset + x] > 0)
{
yield return (x, y);
}
}
}
}
private IPrimitive GetTraceData(IEnumerable<IObject3D> supportCandidates)
{
List<Vector3Float> supportVerts;
FaceList supportFaces;
// find all the faces that are candidates for support
supportVerts = new List<Vector3Float>();
supportFaces = new FaceList();
foreach (var item in supportCandidates)
{
// add all the down faces to supportNeededImage
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)));
// check if the face is pointing in the up direction at all
bool isUpFace = angle > 90;
// check if the face is pointing down
if (angle < MaxOverHangAngle
|| isUpFace)
{
var face = item.Mesh.Faces[faceIndex];
var verts = new int[] { face.v0, face.v1, face.v2 };
var p0 = item.Mesh.Vertices[face.v0].Transform(matrix);
var p1 = item.Mesh.Vertices[face.v1].Transform(matrix);
var p2 = item.Mesh.Vertices[face.v2].Transform(matrix);
var vc = supportVerts.Count;
supportVerts.Add(p0);
supportVerts.Add(p1);
supportVerts.Add(p2);
supportFaces.Add(vc, vc + 1, vc + 2, face0Normal);
}
}
// add all mesh edges that need support
// add all unsupported vertices (low points of a face group that individually do not need support)
}
return supportFaces.CreateTraceData(supportVerts);
}
}
}

View file

@ -61,9 +61,20 @@ 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 = supportGenerator.PillarSize;
@ -99,19 +110,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
overHangRow.AddChild(overHangField.Content);
this.AddChild(overHangRow);
// Add an editor field for the SupportGenerator.SupportType
PropertyInfo propertyInfo = typeof(SupportGenerator).GetProperty(nameof(SupportGenerator.SupportType));
var editor = PublicPropertyEditor.CreatePropertyEditor(
new EditableProperty(propertyInfo, supportGenerator),
null,
new PPEContext(),
theme);
if (editor != null)
{
this.AddChild(editor);
}
// Button Row
var buttonRow = new FlowLayoutWidget()
{

View file

@ -44,6 +44,7 @@ namespace MatterHackers.MatterControl
public const string NotificationEmailAddress = nameof(NotificationEmailAddress);
public const string NotificationPhoneNumber = nameof(NotificationPhoneNumber);
public const string OpenScadPath = nameof(OpenScadPath);
public const string SupportGenerationType = nameof(SupportGenerationType);
public const string SupportMaxOverHangAngle = nameof(SupportMaxOverHangAngle);
public const string SupportPillarSize = nameof(SupportPillarSize);
public const string PopupLibraryWidth = nameof(PopupLibraryWidth);