commit
72da60062a
6 changed files with 1256 additions and 392 deletions
|
|
@ -33,7 +33,6 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.Image;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.MatterControl.PartPreviewWindow;
|
||||
using MatterHackers.PolygonMesh;
|
||||
|
|
@ -42,46 +41,11 @@ using MatterHackers.VectorMath;
|
|||
|
||||
namespace MatterHackers.MatterControl.DesignTools
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[HideFromTreeViewAttribute, Immutable]
|
||||
public class GeneratedSupportObject3D : Object3D
|
||||
{
|
||||
public GeneratedSupportObject3D()
|
||||
{
|
||||
OutputType = PrintOutputTypes.Support;
|
||||
}
|
||||
}
|
||||
|
||||
public class SupportGenerator
|
||||
{
|
||||
private double minimumSupportHeight;
|
||||
private InteractiveScene scene;
|
||||
private readonly InteractiveScene scene;
|
||||
|
||||
private readonly double minimumSupportHeight;
|
||||
|
||||
public SupportGenerator(InteractiveScene scene, double minimumSupportHeight)
|
||||
{
|
||||
|
|
@ -89,7 +53,16 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
this.scene = scene;
|
||||
}
|
||||
|
||||
public enum SupportGenerationType { Normal, From_Bed }
|
||||
public enum SupportGenerationType
|
||||
{
|
||||
Normal,
|
||||
From_Bed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount to reduce the pillars so they are separated in the 3D view.
|
||||
/// </summary>
|
||||
public static double ColumnReduceAmount => 1;
|
||||
|
||||
public double MaxOverHangAngle
|
||||
{
|
||||
|
|
@ -99,11 +72,13 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
{
|
||||
return 45;
|
||||
}
|
||||
|
||||
var value = UserSettings.Instance.GetValue<double>(UserSettingsKey.SupportMaxOverHangAngle);
|
||||
if (value < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (value > 90)
|
||||
{
|
||||
value = 90;
|
||||
|
|
@ -130,6 +105,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
UserSettings.Instance.set(UserSettingsKey.SupportPillarSize, value.ToString());
|
||||
|
|
@ -155,10 +131,17 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount to reduce the pillars so they are separated in the 3D view
|
||||
/// </summary>
|
||||
public static double ColumnReduceAmount => 1;
|
||||
public static IPrimitive CreateTraceData(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);
|
||||
}
|
||||
|
||||
public Task Create(IProgress<ProgressStatus> progress, CancellationToken cancelationToken)
|
||||
{
|
||||
|
|
@ -166,14 +149,16 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
using (new SelectionMaintainer(scene))
|
||||
{
|
||||
ProgressStatus status = new ProgressStatus();
|
||||
status.Status = "Enter";
|
||||
var status = new ProgressStatus
|
||||
{
|
||||
Status = "Enter"
|
||||
};
|
||||
progress?.Report(status);
|
||||
|
||||
// Get visible meshes for each of them
|
||||
// Get visible meshes for each of them
|
||||
var allBedItems = scene.Children.SelectMany(i => i.VisibleMeshes());
|
||||
|
||||
AxisAlignedBoundingBox suppoortBounds = AxisAlignedBoundingBox.Empty();
|
||||
var suppoortBounds = AxisAlignedBoundingBox.Empty();
|
||||
if (selectedItem != null)
|
||||
{
|
||||
foreach (var candidate in selectedItem.VisibleMeshes())
|
||||
|
|
@ -289,6 +274,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (aboveBed)
|
||||
{
|
||||
var face0Normal = item.Mesh.Faces[faceIndex].normal.TransformNormal(matrix).GetNormal();
|
||||
|
|
@ -314,6 +300,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
// less than 10 micros high, don't ad it
|
||||
return;
|
||||
}
|
||||
|
||||
var support = new GeneratedSupportObject3D()
|
||||
{
|
||||
Mesh = PlatonicSolids.CreateCube(1, 1, 1)
|
||||
|
|
@ -326,80 +313,37 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
holder.Children.Add(support);
|
||||
}
|
||||
|
||||
private void AddSupportColumns(RectangleDouble gridBounds, Dictionary<(int x, int y), List<(double z, bool bottom)>> detectedPlanes)
|
||||
private void AddSupportColumns(RectangleDouble gridBounds, Dictionary<(int x, int y), SupportColumn> supportGrid)
|
||||
{
|
||||
IObject3D supportColumnsToAdd = new Object3D();
|
||||
bool fromBed = SupportType == SupportGenerationType.From_Bed;
|
||||
var halfPillar = PillarSize / 2;
|
||||
foreach (var kvp in detectedPlanes)
|
||||
foreach (var kvp in supportGrid)
|
||||
{
|
||||
var planes = kvp.Value;
|
||||
var supportColumnData = kvp.Value;
|
||||
|
||||
if (planes.Count == 0)
|
||||
if (supportColumnData.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
planes.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)
|
||||
{
|
||||
var nextPlaneIsBottom = planes.Count > 1 && planes[1].bottom;
|
||||
if (!nextPlaneIsBottom // if the next plane is a top, we don't have any space from the bed to the part to put support
|
||||
|| planes[1].z > minimumSupportHeight) // if the next plane is a bottom and is not far enough away, there is no space to put any support
|
||||
// if the next plane is a bottom and is not far enough away, there is no space to put any support
|
||||
if (supportColumnData[0].start < minimumSupportHeight
|
||||
&& supportColumnData[0].end > minimumSupportHeight)
|
||||
{
|
||||
var firstBottomAboveBed = GetNextBottom(0, planes, minimumSupportHeight);
|
||||
|
||||
if (firstBottomAboveBed >= 0)
|
||||
{
|
||||
AddSupportColumn(supportColumnsToAdd, xPos, yPos, 0, planes[firstBottomAboveBed].z + .01);
|
||||
}
|
||||
AddSupportColumn(supportColumnsToAdd, xPos, yPos, 0, supportColumnData[0].end + .01);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
double lastTopZ = 0;
|
||||
int lastBottom = -1;
|
||||
var nextPlaneIsBottom = planes.Count > 1 && planes[1].bottom;
|
||||
// if the next plane (the one above the bed) is a bottom, we have a part on the bed and will not generate support
|
||||
if (nextPlaneIsBottom && planes[1].z <= minimumSupportHeight)
|
||||
foreach (var (start, end) in supportColumnData)
|
||||
{
|
||||
// go up to the next top
|
||||
i = GetNextTop(i, planes, minimumSupportHeight);
|
||||
if (i >= 0)
|
||||
{
|
||||
lastTopZ = planes[i].z;
|
||||
}
|
||||
}
|
||||
|
||||
while (i != -1
|
||||
&& i != lastBottom
|
||||
&& i < planes.Count)
|
||||
{
|
||||
lastBottom = i;
|
||||
// find all open areas in the list and add support
|
||||
i = GetNextBottom(i, planes, minimumSupportHeight);
|
||||
if (i >= 0)
|
||||
{
|
||||
if (i < planes.Count
|
||||
&& planes[i].bottom)
|
||||
{
|
||||
AddSupportColumn(supportColumnsToAdd, xPos, yPos, lastTopZ, planes[i].z);
|
||||
}
|
||||
i = GetNextTop(i + 1, planes, minimumSupportHeight);
|
||||
if (i >= 0
|
||||
&& i < planes.Count)
|
||||
{
|
||||
lastTopZ = planes[i].z;
|
||||
}
|
||||
}
|
||||
AddSupportColumn(supportColumnsToAdd, xPos, yPos, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -407,7 +351,32 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
scene.UndoBuffer.AddAndDo(new InsertCommand(scene, supportColumnsToAdd.Children, false));
|
||||
}
|
||||
|
||||
private Dictionary<(int x, int y), List<(double z, bool bottom)>> DetectRequiredSupportByTracing(RectangleDouble gridBounds, IEnumerable<IObject3D> supportCandidates)
|
||||
private void AddSupportFaces(IEnumerable<IObject3D> supportCandidates, List<Vector3Float> supportVerts, FaceList supportFaces)
|
||||
{
|
||||
foreach (var item in supportCandidates)
|
||||
{
|
||||
// add all the faces
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<(int x, int y), SupportColumn> DetectRequiredSupportByTracing(RectangleDouble gridBounds, IEnumerable<IObject3D> supportCandidates)
|
||||
{
|
||||
var allBounds = supportCandidates.GetAxisAlignedBoundingBox();
|
||||
var rayStartZ = allBounds.MinXYZ.Z - 1;
|
||||
|
|
@ -415,148 +384,81 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
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)>>();
|
||||
var supportColumnData = new Dictionary<(int x, int y), SupportColumn>();
|
||||
|
||||
int gridWidth = (int)gridBounds.Width;
|
||||
int gridHeight = (int)gridBounds.Height;
|
||||
|
||||
var offset = new Vector3(.000013, .00027, 0);
|
||||
|
||||
// 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++)
|
||||
{
|
||||
// add a single plane at the bed so we always know the bed is a top
|
||||
detectedPlanes.Add((x, y), new List<(double z, bool bottom)>());
|
||||
detectedPlanes[(x, y)].Add((0, false));
|
||||
|
||||
IntersectInfo upHit = null;
|
||||
var supportColumn = new SupportColumn(minimumSupportHeight);
|
||||
supportColumnData.Add((x, y), supportColumn);
|
||||
|
||||
// create support plans at this xy
|
||||
for (double yOffset = -1; yOffset <= 1; yOffset++)
|
||||
{
|
||||
for (double xOffset = -1; xOffset <= 1; xOffset++)
|
||||
{
|
||||
var thisTracePlanes = new HitPlanes(minimumSupportHeight);
|
||||
var halfPillar = PillarSize / 2;
|
||||
var yPos = (gridBounds.Bottom + y) * PillarSize + halfPillar + (yOffset * halfPillar);
|
||||
var xPos = (gridBounds.Left + x) * PillarSize + halfPillar + (xOffset * halfPillar);
|
||||
|
||||
// detect all the bottom plans (surfaces that might need support
|
||||
var upRay = new Ray(new Vector3(xPos + .000013, yPos - .00027, rayStartZ), Vector3.UnitZ, intersectionType: IntersectionType.FrontFace);
|
||||
var upRay = new Ray(new Vector3(xPos, yPos, rayStartZ) + offset, Vector3.UnitZ, intersectionType: IntersectionType.FrontFace);
|
||||
IntersectInfo upHit;
|
||||
do
|
||||
{
|
||||
upHit = traceData.GetClosestIntersection(upRay);
|
||||
if (upHit != null)
|
||||
{
|
||||
detectedPlanes[(x, y)].Add((upHit.HitPosition.Z, true));
|
||||
var angle = MathHelper.RadiansToDegrees(Math.Acos(upHit.normalAtHit.Dot(-Vector3.UnitZ)));
|
||||
thisTracePlanes.Add(new HitPlane(upHit.HitPosition.Z, angle));
|
||||
|
||||
// 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.FrontFace);
|
||||
upRay = new Ray(new Vector3(xPos, yPos, upHit.HitPosition.Z + .001) + offset, Vector3.UnitZ, intersectionType: IntersectionType.FrontFace);
|
||||
}
|
||||
} while (upHit != null);
|
||||
}
|
||||
while (upHit != null);
|
||||
|
||||
// detect all the up plans (surfaces that will have support on top of them)
|
||||
upRay = new Ray(new Vector3(xPos + .000013, yPos - .00027, rayStartZ), Vector3.UnitZ, intersectionType: IntersectionType.BackFace);
|
||||
upRay = new Ray(new Vector3(xPos, yPos, rayStartZ) + offset, Vector3.UnitZ, intersectionType: IntersectionType.BackFace);
|
||||
do
|
||||
{
|
||||
upHit = traceData.GetClosestIntersection(upRay);
|
||||
if (upHit != null)
|
||||
{
|
||||
detectedPlanes[(x, y)].Add((upHit.HitPosition.Z, false));
|
||||
var angle = MathHelper.RadiansToDegrees(Math.Acos(upHit.normalAtHit.Dot(-Vector3.UnitZ)));
|
||||
thisTracePlanes.Add(new HitPlane(upHit.HitPosition.Z, angle));
|
||||
|
||||
// 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.BackFace);
|
||||
upRay = new Ray(new Vector3(xPos, yPos, upHit.HitPosition.Z + .001) + offset, Vector3.UnitZ, intersectionType: IntersectionType.BackFace);
|
||||
}
|
||||
} while (upHit != null);
|
||||
}
|
||||
while (upHit != null);
|
||||
|
||||
var debugPlanes = new HitPlanes(minimumSupportHeight);
|
||||
debugPlanes.AddRange(thisTracePlanes);
|
||||
debugPlanes.Sort(MaxOverHangAngle);
|
||||
var lineSupport = new SupportColumn(thisTracePlanes, minimumSupportHeight, MaxOverHangAngle);
|
||||
|
||||
if (lineSupport.Count > 0)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
|
||||
supportColumn.Union(lineSupport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return detectedPlanes;
|
||||
}
|
||||
|
||||
public static int GetNextBottom(int i, List<(double z, bool bottom)> planes, double skipDist)
|
||||
{
|
||||
while (i < planes.Count)
|
||||
{
|
||||
// if we are on a bottom
|
||||
if (planes[i].bottom)
|
||||
{
|
||||
// move up to the next plane and re-evaluate
|
||||
i++;
|
||||
}
|
||||
else // we are on a top
|
||||
{
|
||||
// if the next plane is a bottom and more than skipDistanc away
|
||||
if (i + 1 < planes.Count
|
||||
&& planes[i + 1].bottom
|
||||
&& planes[i + 1].z > planes[i].z + skipDist)
|
||||
{
|
||||
// this is the next bottom we are looking for
|
||||
return i + 1;
|
||||
}
|
||||
else // move up to the next plane and re-evaluate
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int GetNextTop(int i, List<(double z, bool bottom)> planes, double skipDist)
|
||||
{
|
||||
if (!planes[i].bottom)
|
||||
{
|
||||
// skip the one we are
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i < planes.Count)
|
||||
{
|
||||
// if we are on a bottom
|
||||
if (planes[i].bottom)
|
||||
{
|
||||
// move up to the next plane and re-evaluate
|
||||
i++;
|
||||
}
|
||||
else // we are on a top
|
||||
{
|
||||
// if the next plane is a bottom and more than skipDistanc away
|
||||
if (i + 1 < planes.Count
|
||||
&& planes[i + 1].bottom
|
||||
&& planes[i + 1].z > planes[i].z + skipDist)
|
||||
{
|
||||
// this is the next top we are looking for
|
||||
return i;
|
||||
}
|
||||
else // move up to the next plane and re-evaluate
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return supportColumnData;
|
||||
}
|
||||
|
||||
private IPrimitive GetTraceData(IEnumerable<IObject3D> supportCandidates)
|
||||
|
|
@ -568,48 +470,387 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
supportVerts = new List<Vector3Float>();
|
||||
supportFaces = new FaceList();
|
||||
|
||||
// find all the down faces from the support candidates
|
||||
// find all the faces from the support candidates
|
||||
AddSupportFaces(supportCandidates,
|
||||
supportVerts,
|
||||
supportFaces,
|
||||
(angle) => angle <= MaxOverHangAngle);
|
||||
supportFaces);
|
||||
|
||||
// find all the up faces from everything on the bed
|
||||
AddSupportFaces(scene.Children.SelectMany(i => i.VisibleMeshes()),
|
||||
supportVerts,
|
||||
supportFaces,
|
||||
(angle) => angle >= 90);
|
||||
|
||||
return supportFaces.CreateTraceData(supportVerts);
|
||||
return CreateTraceData(supportFaces, supportVerts);
|
||||
}
|
||||
|
||||
private void AddSupportFaces(IEnumerable<IObject3D> supportCandidates, List<Vector3Float> supportVerts, FaceList supportFaces, Func<double, bool> doAdd)
|
||||
public struct HitPlane
|
||||
{
|
||||
foreach (var item in supportCandidates)
|
||||
public double Z;
|
||||
|
||||
public HitPlane(double z, bool bottom)
|
||||
: this(z, bottom ? 0 : 180)
|
||||
{
|
||||
// add all the down faces to supportNeededImage
|
||||
var matrix = item.WorldMatrix(scene);
|
||||
for (int faceIndex = 0; faceIndex < item.Mesh.Faces.Count; faceIndex++)
|
||||
}
|
||||
|
||||
public HitPlane(double z, double angle)
|
||||
{
|
||||
this.Z = z;
|
||||
|
||||
this.Angle = angle;
|
||||
}
|
||||
|
||||
public bool Bottom(double maxOverHangAngle = 45)
|
||||
{
|
||||
return Angle <= maxOverHangAngle;
|
||||
}
|
||||
|
||||
public double Angle { get; set; }
|
||||
|
||||
public bool Top(double maxOverHangAngle = 45)
|
||||
{
|
||||
return Angle > maxOverHangAngle;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Z={Z:0.###} {(Bottom(45) ? "Bottom" : "Top")}";
|
||||
}
|
||||
}
|
||||
|
||||
[HideFromTreeViewAttribute, Immutable]
|
||||
public class GeneratedSupportObject3D : Object3D
|
||||
{
|
||||
public GeneratedSupportObject3D()
|
||||
{
|
||||
OutputType = PrintOutputTypes.Support;
|
||||
}
|
||||
}
|
||||
|
||||
public class SupportColumn : List<(double start, double end)>
|
||||
{
|
||||
private readonly double minimumSupportHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SupportColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="minimumSupportHeight">The minimum distance between support regions.</param>
|
||||
public SupportColumn(double minimumSupportHeight)
|
||||
{
|
||||
this.minimumSupportHeight = minimumSupportHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SupportColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="inputPlanes">The planes to consider while creating the support regions.</param>
|
||||
/// <param name="minimumSupportHeight">The minimum distance between support regions.</param>
|
||||
/// <param name="maxOverHangAngle">The maximum angle that will be treated as a bottom.</param>
|
||||
public SupportColumn(HitPlanes inputPlanes, double minimumSupportHeight, double maxOverHangAngle = 45)
|
||||
: this(minimumSupportHeight)
|
||||
{
|
||||
var hitPlanes = new HitPlanes(inputPlanes.MinimumSupportHeight);
|
||||
hitPlanes.AddRange(inputPlanes);
|
||||
hitPlanes.Simplify(maxOverHangAngle);
|
||||
|
||||
var i = 0;
|
||||
var currentTop = 0.0;
|
||||
// if the first bottom is more than the min distance
|
||||
if (hitPlanes.Count > 1 && hitPlanes[i].Z <= minimumSupportHeight)
|
||||
{
|
||||
var face0Normal = item.Mesh.Faces[faceIndex].normal.TransformNormal(matrix).GetNormal();
|
||||
var angle = MathHelper.RadiansToDegrees(Math.Acos(face0Normal.Dot(-Vector3Float.UnitZ)));
|
||||
currentTop = hitPlanes[i + 1].Z;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (doAdd(angle))
|
||||
for (; i < hitPlanes.Count / 2 * 2; i += 2)
|
||||
{
|
||||
if (hitPlanes[i].Z > currentTop + minimumSupportHeight)
|
||||
{
|
||||
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);
|
||||
this.Add((currentTop, hitPlanes[i].Z));
|
||||
currentTop = hitPlanes[i + 1].Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
supportFaces.Add(vc, vc + 1, vc + 2, face0Normal);
|
||||
public void Union(SupportColumn other)
|
||||
{
|
||||
if (this.Count == 0)
|
||||
{
|
||||
this.AddRange(other);
|
||||
return;
|
||||
}
|
||||
|
||||
// merge them, considering minimumSupportHeight
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < other.Count; j++)
|
||||
{
|
||||
// check if they overlap and other is not completely contained in this
|
||||
if (this[i].start <= other[j].end + minimumSupportHeight
|
||||
&& this[i].end >= other[j].start - minimumSupportHeight
|
||||
&& (this[i].start > other[j].start || this[i].end < other[j].end))
|
||||
{
|
||||
// set this range to be the union
|
||||
this[i] = (Math.Min(this[i].start, other[j].start),
|
||||
Math.Max(this[i].end, other[j].end));
|
||||
// fix up the planes in this
|
||||
this.RemoveOverLaps();
|
||||
// and start at the beginning again
|
||||
i--;
|
||||
// drop out of the j loop
|
||||
break;
|
||||
}
|
||||
else if (this[i].end < other[j].start
|
||||
&& i < this.Count - 1
|
||||
&& this[i + 1].start > other[j].end)
|
||||
{
|
||||
// we are beyond the end of this
|
||||
// add every additional set and return
|
||||
this.Insert(i + 1, other[j]);
|
||||
this.RemoveOverLaps();
|
||||
i--;
|
||||
// drop out of the j loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveOverLaps()
|
||||
{
|
||||
// merge them, considering minimumSupportHeight
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
for (int j = i + 1; j < this.Count; j++)
|
||||
{
|
||||
// check this is an overlap with the next segment
|
||||
if (this[i].start <= this[j].end + minimumSupportHeight
|
||||
&& this[i].end >= this[j].start - minimumSupportHeight
|
||||
&& (this[i].start >= this[j].start || this[i].end <= this[j].end))
|
||||
{
|
||||
// set this range to be the union
|
||||
this[i] = (Math.Min(this[i].start, this[j].start),
|
||||
Math.Max(this[i].end, this[j].end));
|
||||
// fix up the planes in this
|
||||
this.RemoveAt(j);
|
||||
// and start at the beginning again
|
||||
i--;
|
||||
// drop out of the j loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HitPlanes : List<HitPlane>
|
||||
{
|
||||
public double MinimumSupportHeight { get; private set; }
|
||||
|
||||
public HitPlanes(double minimumSupportHeight)
|
||||
{
|
||||
this.MinimumSupportHeight = minimumSupportHeight;
|
||||
}
|
||||
|
||||
public int GetNextBottom(int i, double maxOverHangAngle = 45)
|
||||
{
|
||||
while (i < this.Count)
|
||||
{
|
||||
// if we are on a bottom
|
||||
if (this[i].Bottom(maxOverHangAngle))
|
||||
{
|
||||
// move up to the next plane and re-evaluate
|
||||
i++;
|
||||
}
|
||||
else // we are on a top
|
||||
{
|
||||
// if the next plane is a bottom and more than minimumSupportHeight away
|
||||
if (i + 1 < this.Count
|
||||
&& this[i + 1].Bottom(maxOverHangAngle)
|
||||
&& this[i + 1].Z > this[i].Z + MinimumSupportHeight)
|
||||
{
|
||||
// this is the next bottom we are looking for
|
||||
return i + 1;
|
||||
}
|
||||
else // move up to the next plane and re-evaluate
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int GetNextTop(int start, double maxOverHangAngle = 45)
|
||||
{
|
||||
var i = start;
|
||||
|
||||
if (this.Count > 0
|
||||
&& !this[i].Bottom(maxOverHangAngle))
|
||||
{
|
||||
// skip the one we are
|
||||
i++;
|
||||
}
|
||||
|
||||
return AdvanceToTop(i, start, maxOverHangAngle);
|
||||
}
|
||||
|
||||
public new void Sort()
|
||||
{
|
||||
throw new NotImplementedException("Call Sort(double maxOverHangAngle) instead");
|
||||
}
|
||||
|
||||
public void Sort(double maxOverHangAngle)
|
||||
{
|
||||
this.Sort((a, b) =>
|
||||
{
|
||||
// one is a top and the other is a bottom, sort by tops first
|
||||
if (((a.Top(maxOverHangAngle) && b.Bottom(maxOverHangAngle)) || (a.Bottom(maxOverHangAngle) && b.Top(maxOverHangAngle)))
|
||||
&& a.Z < b.Z + MinimumSupportHeight / 2
|
||||
&& a.Z > b.Z - MinimumSupportHeight / 2)
|
||||
{
|
||||
return a.Top(MinimumSupportHeight) ? 1 : -1;
|
||||
}
|
||||
|
||||
return a.Z.CompareTo(b.Z);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify the list to have Bottom - Top, Bottom - Top items exactly.
|
||||
/// Remove any internal Planes that are not required. This may reduce the set to no items.
|
||||
/// </summary>
|
||||
/// <param name="maxOverHangAngle">The max angle to consider a bottom.</param>
|
||||
public void Simplify(double maxOverHangAngle = 45)
|
||||
{
|
||||
// sort the list on Z
|
||||
this.Sort(maxOverHangAngle);
|
||||
|
||||
var highestPlane = double.NegativeInfinity;
|
||||
var lastRemoveWasBottom = false;
|
||||
// remove anything that is below 0
|
||||
while (Count > 0
|
||||
&& this[0].Z < 0)
|
||||
{
|
||||
if (this[0].Z > highestPlane)
|
||||
{
|
||||
highestPlane = this[0].Z;
|
||||
lastRemoveWasBottom = this[0].Bottom(maxOverHangAngle);
|
||||
}
|
||||
|
||||
this.RemoveAt(0);
|
||||
}
|
||||
|
||||
// if the first item is a top then add a bottom at 0
|
||||
if ((Count > 0 && this[0].Top(maxOverHangAngle)
|
||||
&& this[0].Z > 0)
|
||||
|| lastRemoveWasBottom)
|
||||
{
|
||||
this.Insert(0, new HitPlane(0, true));
|
||||
}
|
||||
|
||||
// if the first item is still a top, remove it
|
||||
while (Count > 0
|
||||
&& this[0].Top(maxOverHangAngle))
|
||||
{
|
||||
this.RemoveAt(0);
|
||||
}
|
||||
|
||||
// remove any items that are between a bottom and a top
|
||||
int currentBottom = 0;
|
||||
|
||||
while (Count > currentBottom
|
||||
&& currentBottom != -1)
|
||||
{
|
||||
var top = GetNextTop(currentBottom, maxOverHangAngle);
|
||||
if (top != -1)
|
||||
{
|
||||
// remove everything between the bottom and the top
|
||||
for (int i = top - 1; i > currentBottom; i--)
|
||||
{
|
||||
this.RemoveAt(i);
|
||||
}
|
||||
|
||||
top = currentBottom + 1;
|
||||
if (this[top].Z - this[currentBottom].Z < MinimumSupportHeight)
|
||||
{
|
||||
// also remove the top
|
||||
this.RemoveAt(top);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the bottom up past the current top
|
||||
currentBottom = GetNextBottom(top, maxOverHangAngle);
|
||||
|
||||
// remove everything between the bottom and the new top
|
||||
if (currentBottom != -1)
|
||||
{
|
||||
for (int i = currentBottom - 1; i > top; i--)
|
||||
{
|
||||
this.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
currentBottom = top + 1;
|
||||
}
|
||||
}
|
||||
else // not another top
|
||||
{
|
||||
// if the last plane is a bottom add a top above it at the minimum distance
|
||||
if (this.Count > 0
|
||||
&& this[this.Count - 1].Bottom(maxOverHangAngle))
|
||||
{
|
||||
var topHeight = this[this.Count - 1].Z + MinimumSupportHeight;
|
||||
// remove all the bottoms from current up to last (but keep the actual last)
|
||||
for (int i = this.Count - 1; i > currentBottom; i--)
|
||||
{
|
||||
this.RemoveAt(i);
|
||||
}
|
||||
|
||||
// add a top
|
||||
this.Add(new HitPlane(topHeight, false));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int AdvanceToTop(int i, int start, double maxOverHangAngle)
|
||||
{
|
||||
while (i < this.Count)
|
||||
{
|
||||
// if we are on a bottom
|
||||
if (this[i].Bottom(maxOverHangAngle))
|
||||
{
|
||||
// move up to the next plane and re-evaluate
|
||||
i++;
|
||||
}
|
||||
else // we are on a top
|
||||
{
|
||||
// if the next plane is a bottom and more than minimumSupportHeight away
|
||||
if (i + 1 < this.Count
|
||||
&& this[i + 1].Bottom(maxOverHangAngle)
|
||||
&& this[i + 1].Z > this[i].Z + MinimumSupportHeight)
|
||||
{
|
||||
// this is the next top we are looking for
|
||||
return i;
|
||||
}
|
||||
else // move up to the next plane and re-evaluate
|
||||
{
|
||||
// if we started on a bottom
|
||||
// and we are the last top
|
||||
// and we are far enough away from the start bottom
|
||||
if (this[start].Bottom(maxOverHangAngle)
|
||||
&& i == this.Count - 1
|
||||
&& this[i].Z - this[start].Z > MinimumSupportHeight)
|
||||
{
|
||||
// we are on the last top of the part and have move up from some other part
|
||||
return i;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -447,7 +447,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
foreach (var child in sceneContext.Scene.Children)
|
||||
{
|
||||
if (child is GeneratedSupportObject3D)
|
||||
if (child is SupportGenerator.GeneratedSupportObject3D)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
|
|||
// TODO: Use existing AssetsPath property
|
||||
string assetsDirectory = Path.Combine(ApplicationDataStorage.Instance.ApplicationLibraryDataPath, "Assets");
|
||||
var itemWorldMatrix = item.WorldMatrix();
|
||||
if (item is GeneratedSupportObject3D generatedSupportObject3D)
|
||||
if (item is SupportGenerator.GeneratedSupportObject3D generatedSupportObject3D)
|
||||
{
|
||||
// grow the support columns by the amount they are reduced by
|
||||
var aabb = item.GetAxisAlignedBoundingBox();
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 95d5991cf3325b19dec5b1ece1fad940989a7039
|
||||
Subproject commit cd05ff5df6b486fcf3bae2bcef848021a52d1046
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 42e6652050dfce45e2c158509839db15eb8a5886
|
||||
Subproject commit da3b47918b41e20d1bdd9c477cf9a09325cf6a9a
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue