More work on probe validation

Started work on a new boolean operation stack
This commit is contained in:
Lars Brubaker 2021-11-05 18:08:58 -07:00
parent fe31aaca05
commit 03890b1155
7 changed files with 454 additions and 116 deletions

View file

@ -34,16 +34,21 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using ClipperLib;
using DualContouring;
using g3;
using gs;
using MatterHackers.Agg;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.PolygonMesh;
using MatterHackers.PolygonMesh.Csg;
using MatterHackers.VectorMath;
namespace MatterHackers.PolygonMesh
{
using Polygon = List<IntPoint>;
using Polygons = List<List<IntPoint>>;
public static class BooleanProcessing
{
public enum CsgModes
@ -64,6 +69,7 @@ namespace MatterHackers.PolygonMesh
public enum ProcessingModes
{
Polygons,
Polygons2,
Marching_Cubes,
Dual_Contouring,
}
@ -142,6 +148,102 @@ namespace MatterHackers.PolygonMesh
return resultsMesh;
}
else if (processingMode == ProcessingModes.Polygons2)
{
var progressStatus = new ProgressStatus();
var totalOperations = 0;
foreach (var item in items)
{
totalOperations += item.mesh.Faces.Count;
}
double amountPerOperation = 1.0 / totalOperations;
double percentCompleted = 0;
var resultsMesh = new Mesh();
foreach (var item1 in items)
{
var mesh1 = item1.mesh.Copy(CancellationToken.None);
mesh1.Transform(item1.matrix);
foreach (var face in mesh1.Faces)
{
var cutPlane = new Plane(mesh1.Vertices[face.v0].AsVector3(), mesh1.Vertices[face.v1].AsVector3(), mesh1.Vertices[face.v2].AsVector3());
var totalSlice = new Polygons();
var first = true;
foreach (var item2 in items)
{
if (item1 == item2)
{
continue;
}
var mesh2 = item2.mesh.Copy(CancellationToken.None);
mesh2.Transform(item2.matrix);
// calculate and add the PWN face from the loops
var slice = SliceLayer.CreateSlice(mesh2, cutPlane);
if (first)
{
totalSlice = slice;
first = false;
}
else
{
totalSlice.Union(slice);
}
// now we have the total loops that this polygon can intersect from the other meshes
// make a polygon for this face
var rotation = new Quaternion(cutPlane.Normal, Vector3.UnitZ);
var flattenedMatrix = Matrix4X4.CreateRotation(rotation);
flattenedMatrix *= Matrix4X4.CreateTranslation(0, 0, -cutPlane.DistanceFromOrigin);
var meshTo0Plane = flattenedMatrix * Matrix4X4.CreateScale(1000);
var facePolygon = new Polygon();
var intPoint = Vector3Ex.Transform(mesh1.Vertices[face.v0].AsVector3(), meshTo0Plane);
facePolygon.Add(new IntPoint(intPoint.X, intPoint.Y));
intPoint = Vector3Ex.Transform(mesh1.Vertices[face.v1].AsVector3(), meshTo0Plane);
facePolygon.Add(new IntPoint(intPoint.X, intPoint.Y));
intPoint = Vector3Ex.Transform(mesh1.Vertices[face.v2].AsVector3(), meshTo0Plane);
facePolygon.Add(new IntPoint(intPoint.X, intPoint.Y));
var polygonShape = new Polygons();
// clip against the slice based on the parameters
var clipper = new Clipper();
clipper.AddPath(facePolygon, PolyType.ptSubject, true);
clipper.AddPaths(totalSlice, PolyType.ptClip, true);
switch (operation)
{
case CsgModes.Union:
clipper.Execute(ClipType.ctDifference, polygonShape);
break;
case CsgModes.Subtract:
clipper.Execute(ClipType.ctDifference, polygonShape);
break;
case CsgModes.Intersect:
clipper.Execute(ClipType.ctIntersection, polygonShape);
break;
}
// mesh the new polygon and add it to the resultsMesh
polygonShape.Vertices().TriangulateFaces(null, resultsMesh, 0, flattenedMatrix.Inverted);
}
percentCompleted += amountPerOperation;
progressStatus.Progress0To1 = percentCompleted;
reporter?.Report(progressStatus);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
}
}
return resultsMesh;
}
else
{
var implicitMeshs = new List<BoundedImplicitFunction3d>();

View file

@ -192,107 +192,115 @@ namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
printer.Connection.QueueLine($"T0");
}
var pageTitle = this.Title + " " + "Wizard".Localize();
bool probeBeingUsed = printer.Settings.GetValue<bool>(SettingsKey.has_z_probe)
&& printer.Settings.GetValue<bool>(SettingsKey.use_z_probe);
if (probeBeingUsed)
bool doManualOffset = false;
if (doManualOffset)
{
// do the automatic probing of the center position
yield return new AutoProbeFeedback(
this,
probeStartPosition,
"Probe at bed center".Localize(),
"Sample the bed center position to determine the probe distance to the bed".Localize(),
autoProbePositions,
0);
}
if (hotendCount == 1
&& probeBeingUsed
&& printer.Settings.GetValue<bool>(SettingsKey.has_conductive_nozzle)
&& printer.Settings.GetValue<bool>(SettingsKey.measure_probe_offset_conductively))
{
var conductiveProbeFeedback = new ConductiveProbeFeedback(
this,
probeStartPosition,
"Conductive Probing".Localize(),
"Measure the nozzle to probe offset using the conductive pad.".Localize(),
manualProbePositions[0]);
yield return conductiveProbeFeedback;
if (conductiveProbeFeedback.MovedBelowMinZ)
if (probeBeingUsed)
{
// show an error message
// do the automatic probing of the center position
yield return new AutoProbeFeedback(
this,
probeStartPosition,
"Probe at bed center".Localize(),
"Sample the bed center position to determine the probe distance to the bed".Localize(),
autoProbePositions,
0);
}
if (hotendCount == 1
&& probeBeingUsed
&& printer.Settings.GetValue<bool>(SettingsKey.has_conductive_nozzle)
&& printer.Settings.GetValue<bool>(SettingsKey.measure_probe_offset_conductively))
{
var conductiveProbeFeedback = new ConductiveProbeFeedback(
this,
probeStartPosition,
"Conductive Probing".Localize(),
"Measure the nozzle to probe offset using the conductive pad.".Localize(),
manualProbePositions[0]);
yield return conductiveProbeFeedback;
if (conductiveProbeFeedback.MovedBelowMinZ)
{
// show an error message
yield return new WizardPage(
this,
"Error: Below Conductive Probe Min Z".Localize(),
"The printer moved below the minimum height set for conductive probing. Check that the nozzle is clean and there is continuity with the pad.".Localize());
}
else // found a good probe height
{
SetExtruderOffset(autoProbePositions, manualProbePositions, false, 0);
}
}
else // collect the probe information manually
{
// show what steps will be taken
yield return new WizardPage(
this,
"Error: Below Conductive Probe Min Z".Localize(),
"The printer moved below the minimum height set for conductive probing. Check that the nozzle is clean and there is continuity with the pad.".Localize());
}
else // found a good probe height
{
SetExtruderOffset(autoProbePositions, manualProbePositions, false, 0);
}
}
else // collect the probe information manually
{
// show what steps will be taken
yield return new WizardPage(
this,
"Measure the nozzle offset".Localize(),
"{0}:\n\n\t• {1}\n\n{2}\n\n{3}".FormatWith(
"To complete the next few steps you will need".Localize(),
"A sheet of paper".Localize(),
"We will use this paper to measure the distance between the nozzle and the bed.".Localize(),
"Click 'Next' to continue.".Localize()));
"Measure the nozzle offset".Localize(),
"{0}:\n\n\t• {1}\n\n{2}\n\n{3}".FormatWith(
"To complete the next few steps you will need".Localize(),
"A sheet of paper".Localize(),
"We will use this paper to measure the distance between the nozzle and the bed.".Localize(),
"Click 'Next' to continue.".Localize()));
for (int extruderIndex = 0; extruderIndex < hotendCount; extruderIndex++)
{
if (extruderCount > 1)
for (int extruderIndex = 0; extruderIndex < hotendCount; extruderIndex++)
{
// reset the extruder that was active
printer.Connection.QueueLine($"T{extruderIndex}");
if (extruderCount > 1)
{
// reset the extruder that was active
printer.Connection.QueueLine($"T{extruderIndex}");
}
// do the manual probe of the same position
yield return new GetCoarseBedHeight(
this,
new Vector3(probePosition, startProbeHeight),
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"Low Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
yield return new GetFineBedHeight(
this,
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"Medium Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
yield return new GetUltraFineBedHeight(
this,
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"High Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
SetExtruderOffset(autoProbePositions, manualProbePositions, probeBeingUsed, extruderIndex);
}
// do the manual probe of the same position
yield return new GetCoarseBedHeight(
this,
new Vector3(probePosition, startProbeHeight),
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"Low Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
yield return new GetFineBedHeight(
this,
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"Medium Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
yield return new GetUltraFineBedHeight(
this,
string.Format(
"{0} {1} {2} - {3}",
levelingStrings.GetStepString(totalSteps),
"Position".Localize(),
1,
"High Precision".Localize()),
manualProbePositions[extruderIndex],
0,
levelingStrings);
SetExtruderOffset(autoProbePositions, manualProbePositions, probeBeingUsed, extruderIndex);
}
// let the user know we are done with the manual part
yield return new CalibrateProbeRemovePaperInstructions(this, pageTitle, false);
}
if (extruderCount > 1)
@ -307,20 +315,16 @@ namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
babySteppingValue[i] = 0;
}
var pageTitle = this.Title + " " + "Wizard".Localize();
if (hotendCount == 1 // this could be improved for dual extrusion calibration in the future. But for now it is single extrusion.
&& probeBeingUsed
&& printer.Settings.GetValue<bool>(SettingsKey.validate_probe_offset))
&& probeBeingUsed
&& printer.Settings.GetValue<bool>(SettingsKey.validate_probe_offset))
{
// let the user know we are done with the manual part
yield return new CalibrateProbeRemovePaperInstructions(this, pageTitle, false);
// tell them about the automatic part and any settings that should be changed
yield return new ZProbePrintCalibrationPartPage(
this,
printer,
"Validating Z Offset".Localize(),
"We will now improve accuracy by measure the probe offset from the top of a printed calibration object.".Localize());
"We will now measure the probe offset from the top of a printed calibration object.".Localize());
// measure the top of the part we just printed
yield return new ZProbeCalibrateRetrieveTopProbeData(this, pageTitle);
// tell the user we are done and everything should be working

View file

@ -28,14 +28,28 @@ either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using MatterControl.Printing;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.PrinterCommunication;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
{
public class ZProbeCalibrateRetrieveTopProbeData : WizardPage
{
private bool validationRunning;
private bool oldAllowLeveling;
private Vector3 positionToSample;
private List<double> babySteppingValue;
private Vector3 sampledPosition;
private bool waitingToCompleteNextSample;
private bool dataCollected;
private List<double> samplesForSinglePosition;
private Vector3 positionToSampleWithProbeOffset;
public ZProbeCalibrateRetrieveTopProbeData(ISetupWizard setupWizard, string headerText)
: base(setupWizard, headerText, "")
{
@ -44,22 +58,215 @@ namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
contentRow.BackgroundColor = theme.MinimalShade;
}
public override void OnClosed(EventArgs e)
{
CancelValidation();
printer.Connection.CanceleRequested -= Connection_PrintCanceled;
base.OnClosed(e);
}
private void CancelValidation()
{
if (validationRunning)
{
validationRunning = false;
printer.Connection.LineReceived -= GetZProbeHeight;
// If leveling was on when we started, make sure it is on when we are done.
printer.Connection.AllowLeveling = true;
// set the baby stepping back to the last known good value
printer.Settings.ForTools<double>(SettingsKey.baby_step_z_offset, (key, value, i) =>
{
printer.Settings.SetValue(key, babySteppingValue[i].ToString());
});
RetractProbe();
}
}
private void Connection_PrintCanceled(object sender, EventArgs e)
{
CancelValidation();
}
private void RetractProbe()
{
// make sure we raise the probe on close
if (printer.Settings.GetValue<bool>(SettingsKey.has_z_probe)
&& printer.Settings.GetValue<bool>(SettingsKey.use_z_probe)
&& printer.Settings.GetValue<bool>(SettingsKey.has_z_servo))
{
// make sure the servo is retracted
var servoRetract = printer.Settings.GetValue<double>(SettingsKey.z_servo_retracted_angle);
printer.Connection.QueueLine($"M280 P0 S{servoRetract}");
}
}
private void SampleProbePoints()
{
if (waitingToCompleteNextSample)
{
return;
}
double startProbeHeight = printer.Settings.GetValue<double>(SettingsKey.print_leveling_probe_start) + ZProbePrintCalibrationPartPage.CalibrationObjectHeight(printer);
if (!dataCollected)
{
var validProbePosition2D = PrintLevelingWizard.EnsureInPrintBounds(printer, printer.Bed.BedCenter);
positionToSample = new Vector3(validProbePosition2D, startProbeHeight);
this.SamplePoint();
}
else
{
SaveSamplePoints();
CancelValidation();
}
}
private void GetZProbeHeight(object sender, string line)
{
if (line != null)
{
double sampleRead = double.MinValue;
if (line.StartsWith("Bed")) // marlin G30 return code (looks like: 'Bed Position X:20 Y:32 Z:.01')
{
sampledPosition.X = positionToSample.X;
sampledPosition.Y = positionToSample.Y;
GCodeFile.GetFirstNumberAfter("Z:", line, ref sampleRead);
}
else if (line.StartsWith("Z:")) // smoothie G30 return code (looks like: 'Z:10.01')
{
sampledPosition.X = positionToSample.X;
sampledPosition.Y = positionToSample.Y;
// smoothie returns the position relative to the start position
double reportedProbeZ = 0;
GCodeFile.GetFirstNumberAfter("Z:", line, ref reportedProbeZ);
sampleRead = positionToSample.Z - reportedProbeZ;
}
if (sampleRead != double.MinValue)
{
samplesForSinglePosition.Add(sampleRead);
int numberOfSamples = printer.Settings.GetValue<int>(SettingsKey.z_probe_samples);
if (samplesForSinglePosition.Count >= numberOfSamples)
{
samplesForSinglePosition.Sort();
if (samplesForSinglePosition.Count > 3)
{
// drop the high and low values
samplesForSinglePosition.RemoveAt(0);
samplesForSinglePosition.RemoveAt(samplesForSinglePosition.Count - 1);
}
sampledPosition.Z = Math.Round(samplesForSinglePosition.Average(), 2);
// When probe data has been collected, resume our thread to continue collecting
waitingToCompleteNextSample = false;
}
else
{
// add the next request for probe
printer.Connection.QueueLine("G30");
// raise the probe after each sample
var feedRates = printer.Settings.Helpers.ManualMovementSpeeds();
printer.Connection.QueueLine($"G1 X{positionToSampleWithProbeOffset.X:0.###}Y{positionToSampleWithProbeOffset.Y:0.###}Z{positionToSampleWithProbeOffset.Z:0.###} F{feedRates.X}");
}
}
}
}
private void SaveSamplePoints()
{
printer.Settings.ForTools<double>(SettingsKey.baby_step_z_offset, (key, value, i) =>
{
printer.Settings.SetValue(key, "0");
});
printer.Connection.AllowLeveling = oldAllowLeveling;
}
private void SetupForValidation()
{
validationRunning = true;
// make sure baby stepping is removed as this will be calibrated exactly (assuming it works)
printer.Settings.ForTools<double>(SettingsKey.baby_step_z_offset, (key, value, i) =>
{
// remember the current baby stepping values
babySteppingValue[i] = value;
printer.Settings.SetValue(key, "0");
});
oldAllowLeveling = printer.Connection.AllowLeveling;
// turn off print leveling
printer.Connection.AllowLeveling = false;
var levelingData = new PrintLevelingData()
{
LevelingSystem = printer.Settings.GetValue<LevelingSystem>(SettingsKey.print_leveling_solution)
};
}
private void DeployServo()
{
if (printer.Settings.GetValue<bool>(SettingsKey.has_z_servo))
{
// make sure the servo is deployed
var servoDeployCommand = printer.Settings.GetValue<double>(SettingsKey.z_servo_depolyed_angle);
printer.Connection.QueueLine($"M280 P0 S{servoDeployCommand}");
}
}
private Vector3 ProbeOffset
{
get => printer.Settings.GetValue<Vector3>(SettingsKey.probe_offset);
}
private Vector3 FeedRates
{
get => printer.Settings.Helpers.ManualMovementSpeeds();
}
private void SamplePoint()
{
positionToSampleWithProbeOffset = positionToSample;
var feedRates = FeedRates;
var probeOffset = ProbeOffset;
// subtract out the probe offset
// we are only interested in the xy position
probeOffset.Z = 0;
positionToSampleWithProbeOffset -= probeOffset;
printer.Connection.QueueLine($"G1 Z{positionToSample.Z:0.###} F{feedRates.Z}");
printer.Connection.QueueLine($"G1 X{positionToSampleWithProbeOffset.X:0.###}Y{positionToSampleWithProbeOffset.Y:0.###}Z{positionToSampleWithProbeOffset.Z:0.###} F{feedRates.X}");
// probe the current position
printer.Connection.QueueLine("G30");
// raise the probe after each sample
printer.Connection.QueueLine($"G1 X{positionToSampleWithProbeOffset.X:0.###}Y{positionToSampleWithProbeOffset.Y:0.###}Z{positionToSampleWithProbeOffset.Z:0.###} F{feedRates.X}");
}
public override void OnLoad(EventArgs args)
{
// register to listen to the printer
printer.Connection.LineReceived += GetZProbeHeight;
printer.Connection.CanceleRequested += Connection_PrintCanceled;
// we have just completed the print of the calibration object move to the probe position and probe the top
this.NextButton.Enabled = false;
// make sure we are on T0
printer.Connection.QueueLine("T0");
printer.Connection.MoveRelative(PrinterConnection.Axis.X, .1, printer.Settings.Helpers.ManualMovementSpeeds().X);
if (printer.Settings.GetValue<bool>(SettingsKey.z_homes_to_max))
{
printer.Connection.HomeAxis(PrinterConnection.Axis.XYZ);
}
else if (!printer.Settings.GetValue<bool>(SettingsKey.has_z_probe))
{
// Lift the hotend off the bed - at the conclusion of the wizard, make sure we lift the heated nozzle off the bed
printer.Connection.MoveRelative(PrinterConnection.Axis.Z, 2, printer.Settings.Helpers.ManualMovementSpeeds().Z);
}
// Move to the correct z height
printer.Connection.MoveAbsolute(PrinterConnection.Axis.Z, 2, printer.Settings.Helpers.ManualMovementSpeeds().Z);
base.OnLoad(args);
}

View file

@ -115,13 +115,28 @@ namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
private static IObject3D CreateCalibrationObject(PrinterConfig printer)
{
var nozzleDiameter = printer.Settings.GetValue<double>(SettingsKey.nozzle_diameter);
var size = Math.Min(30, nozzleDiameter * 20);
var printObject = new Object3D();
return new Object3D()
var layerHeight = printer.Settings.GetValue<double>(SettingsKey.layer_height);
var baseSize = 20;
var inset = 2.5;
// add a base
var mesh = PlatonicSolids.CreateCube(baseSize, baseSize, CalibrationObjectHeight(printer) - layerHeight);
mesh.Translate(0, 0, mesh.GetAxisAlignedBoundingBox().ZSize / 2);
printObject.Children.Add(new Object3D()
{
Mesh = PlatonicSolids.CreateCube(size, size, CalibrationObjectHeight(printer))
};
Mesh = mesh
});
// add a middle part where we will probe to find the height and the edges of
mesh = PlatonicSolids.CreateCube(baseSize - inset, baseSize - inset, CalibrationObjectHeight(printer));
mesh.Translate(0, 0, mesh.GetAxisAlignedBoundingBox().ZSize / 2);
printObject.Children.Add(new Object3D()
{
Mesh = mesh
});
return printObject;
}
}
}

View file

@ -377,7 +377,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var delta = worldPosition - world.EyePosition;
var deltaLength = delta.Length;
var minDist = 3;
var maxDist = 2000;
var maxDist = 2300;
if ((deltaLength < minDist && zoomDelta < 0)
|| (deltaLength > maxDist && zoomDelta > 0))
{

View file

@ -2822,6 +2822,16 @@ Make sure that your printer is turned on. Some printers will appear to be connec
public bool AllowLeveling
{
get
{
if (printLevelingStream != null)
{
return printLevelingStream.AllowLeveling;
}
return false;
}
set
{
if (printLevelingStream != null)

@ -1 +1 @@
Subproject commit d0699ed8594fd9af0901f6e319c7ae4353b08d51
Subproject commit 2cb0092c6b1500243e5314cb2ad8b504ebff53a7