2014-06-05 11:45:40 -07:00
/ *
2018-04-11 10:22:06 -07:00
Copyright ( c ) 2018 , Lars Brubaker
2014-06-05 11:45:40 -07:00
All rights reserved .
Redistribution and use in source and binary forms , with or without
2015-04-08 15:20:10 -07:00
modification , are permitted provided that the following conditions are met :
2014-06-05 11:45:40 -07:00
1. Redistributions of source code must retain the above copyright notice , this
2015-04-08 15:20:10 -07:00
list of conditions and the following disclaimer .
2014-06-05 11:45:40 -07:00
2. Redistributions in binary form must reproduce the above copyright notice ,
this list of conditions and the following disclaimer in the documentation
2015-04-08 15:20:10 -07:00
and / or other materials provided with the distribution .
2014-06-05 11:45:40 -07:00
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
2015-04-08 15:20:10 -07:00
of the authors and should not be interpreted as representing official policies ,
2014-06-05 11:45:40 -07:00
either expressed or implied , of the FreeBSD Project .
* /
2018-03-29 12:31:36 -07:00
using MatterHackers.Agg ;
2014-06-05 11:45:40 -07:00
using MatterHackers.Agg.UI ;
using MatterHackers.Localizations ;
2018-04-03 18:17:27 -07:00
using MatterHackers.MatterControl.PrinterCommunication.Io ;
2014-06-05 11:45:40 -07:00
using MatterHackers.MatterControl.SlicerConfiguration ;
2015-04-08 15:20:10 -07:00
using MatterHackers.VectorMath ;
using System ;
2017-05-19 14:39:57 -07:00
using System.Collections.Generic ;
2014-06-05 11:45:40 -07:00
2014-06-06 16:05:18 -07:00
namespace MatterHackers.MatterControl.ConfigurationPage.PrintLeveling
2014-06-05 11:45:40 -07:00
{
2018-04-10 17:59:15 -07:00
public class PrintLevelWizardControl : WizardControl
2015-04-08 15:20:10 -07:00
{
2018-03-29 18:12:02 -07:00
protected PrinterConfig printer ;
2018-04-10 17:59:15 -07:00
ThemeConfig theme ;
LevelWizardBase levelWizard ;
2015-04-08 15:20:10 -07:00
2018-04-10 17:59:15 -07:00
public PrintLevelWizardControl ( PrinterConfig printer , LevelWizardBase levelWizard , ThemeConfig theme )
2015-04-08 15:20:10 -07:00
{
2018-04-10 17:59:15 -07:00
this . levelWizard = levelWizard ;
this . theme = theme ;
2017-09-15 12:08:00 -07:00
this . printer = printer ;
2018-04-10 17:59:15 -07:00
}
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
protected override IEnumerator < WizardControlPage > Pages
{
get
2018-03-29 12:31:36 -07:00
{
2018-04-10 17:59:15 -07:00
List < ProbePosition > probePositions = new List < ProbePosition > ( levelWizard . ProbeCount ) ;
for ( int j = 0 ; j < levelWizard . ProbeCount ; j + + )
{
probePositions . Add ( new ProbePosition ( ) ) ;
}
2018-04-06 14:58:25 -07:00
2018-04-10 17:59:15 -07:00
var levelingStrings = new LevelingStrings ( printer . Settings ) ;
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
this . doneButton . Visible = false ;
2018-04-10 14:40:19 -07:00
2018-04-10 17:59:15 -07:00
// If no leveling data has been calculated
bool showWelcomeScreen = printer . Settings . Helpers . GetPrintLevelingData ( ) . SampledPositions . Count = = 0
& & ! ProbeCalibrationWizard . UsingZProbe ( printer ) ;
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
if ( showWelcomeScreen )
2018-04-10 14:40:19 -07:00
{
2018-04-10 17:59:15 -07:00
string part1 = "Congratulations on connecting to your printer. Before starting your first print we need to run a simple calibration procedure." . Localize ( ) ;
string part2 = "The next few screens will walk your through calibrating your printer." . Localize ( ) ;
string requiredPageInstructions = $"{part1}\n\n{part2}" ;
2018-05-23 11:38:46 -07:00
yield return new InstructionsPage ( printer , levelingStrings . initialPrinterSetupStepText , requiredPageInstructions , theme ) ;
2018-04-10 14:40:19 -07:00
}
2018-04-10 17:59:15 -07:00
// To make sure the bed is at the correct temp, put in a filament selection page.
bool hasHeatedBed = printer . Settings . GetValue < bool > ( SettingsKey . has_heated_bed ) ;
bool useZProbe = printer . Settings . Helpers . UseZProbe ( ) ;
int zProbeSamples = printer . Settings . GetValue < int > ( SettingsKey . z_probe_samples ) ;
var secondsPerManualSpot = 10 * 3 ;
var secondsPerAutomaticSpot = 3 * zProbeSamples ;
var secondsToCompleteWizard = levelWizard . ProbeCount * ( useZProbe ? secondsPerAutomaticSpot : secondsPerManualSpot ) ;
secondsToCompleteWizard + = ( hasHeatedBed ? 60 * 3 : 0 ) ;
2018-05-23 11:38:46 -07:00
yield return new InstructionsPage ( printer ,
2018-04-10 17:59:15 -07:00
"Print Leveling Overview" . Localize ( ) ,
levelingStrings . WelcomeText ( levelWizard . ProbeCount , ( int ) Math . Round ( secondsToCompleteWizard / 60.0 ) ) , theme ) ;
// If we need to heat the bed or the extruder, select the current material
if ( hasHeatedBed | | ! useZProbe )
{
var instruction2 = "Please select the material you will be printing with, so we can accurately calibrate the printer." . Localize ( ) ;
2018-04-10 14:40:19 -07:00
2018-04-10 17:59:15 -07:00
yield return new SelectMaterialPage ( printer , "Select Material" . Localize ( ) , $"{instruction2}" , theme ) ;
}
2018-04-03 12:10:33 -07:00
2018-04-10 17:59:15 -07:00
yield return new HomePrinterPage ( printer , this ,
2018-04-11 17:35:34 -07:00
"Homing The Printer" . Localize ( ) ,
2018-04-10 17:59:15 -07:00
levelingStrings . HomingPageInstructions ( useZProbe , hasHeatedBed ) ,
useZProbe , theme ) ;
2018-04-04 13:45:10 -07:00
2018-04-10 17:59:15 -07:00
// figure out the heating requirements
double targetBedTemp = 0 ;
double targetHotendTemp = 0 ;
if ( hasHeatedBed )
2018-04-10 14:40:19 -07:00
{
2018-04-10 17:59:15 -07:00
targetBedTemp = printer . Settings . GetValue < double > ( SettingsKey . bed_temperature ) ;
2018-04-10 14:40:19 -07:00
}
2018-04-10 17:59:15 -07:00
if ( ! useZProbe )
2018-04-10 14:40:19 -07:00
{
2018-04-10 17:59:15 -07:00
targetHotendTemp = printer . Settings . Helpers . ExtruderTemperature ( 0 ) ;
2018-04-10 14:40:19 -07:00
}
2018-04-10 17:59:15 -07:00
if ( targetBedTemp > 0 | | targetHotendTemp > 0 )
2018-04-10 14:40:19 -07:00
{
2018-04-10 17:59:15 -07:00
string heatingInstructions = "" ;
if ( targetBedTemp > 0 & & targetHotendTemp > 0 )
{
// heating both the bed and the hotend
heatingInstructions = $"Waiting for the bed to heat to {targetBedTemp}" . Localize ( ) + "\n"
+ $"and the hotend to heat to {targetHotendTemp}." . Localize ( ) + "\n"
+ "\n"
+ "This will improve the accuracy of print leveling" . Localize ( )
+ "and ensure no filament is stuck to the tip of the extruder." . Localize ( ) + "\n"
+ "\n"
2018-04-11 12:51:51 -07:00
+ "Warning! The tip of the nozzle will be HOT!" . Localize ( ) + "\n"
2018-04-10 17:59:15 -07:00
+ "Avoid contact with your skin." . Localize ( ) ;
}
else if ( targetBedTemp > 0 )
{
// only heating the bed
heatingInstructions = $"Waiting for the bed to heat to {targetBedTemp}." . Localize ( ) + "\n"
+ "This will improve the accuracy of print leveling." . Localize ( ) ;
}
else // targetHotendTemp > 0
{
// only heating the hotend
heatingInstructions + = $"Waiting for the hotend to heat to {targetHotendTemp}." . Localize ( ) + "\n"
+ "This will ensure no filament is stuck to the tip." . Localize ( ) + "\n"
+ "\n"
2018-04-11 12:51:51 -07:00
+ "Warning! The tip of the nozzle will be HOT!" . Localize ( ) + "\n"
2018-04-10 17:59:15 -07:00
+ "Avoid contact with your skin." . Localize ( ) ;
}
yield return new WaitForTempPage ( printer , this ,
"Waiting For Printer To Heat" . Localize ( ) , heatingInstructions ,
targetBedTemp , targetHotendTemp ,
theme ) ;
2018-04-10 14:40:19 -07:00
}
2018-04-10 17:59:15 -07:00
string positionLabel = "Position" . Localize ( ) ;
string autoCalibrateLabel = "Auto Calibrate" . Localize ( ) ;
string lowPrecisionLabel = "Low Precision" . Localize ( ) ;
string medPrecisionLabel = "Medium Precision" . Localize ( ) ;
string highPrecisionLabel = "High Precision" . Localize ( ) ;
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
double bedRadius = Math . Min ( printer . Settings . GetValue < Vector2 > ( SettingsKey . bed_size ) . X , printer . Settings . GetValue < Vector2 > ( SettingsKey . bed_size ) . Y ) / 2 ;
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
double startProbeHeight = printer . Settings . GetValue < double > ( SettingsKey . print_leveling_probe_start ) ;
int i = 0 ;
foreach ( var goalProbePosition in levelWizard . GetPrintLevelPositionToSample ( ) )
{
var validProbePosition = EnsureInPrintBounds ( printer . Settings , goalProbePosition ) ;
if ( printer . Settings . Helpers . UseZProbe ( ) )
{
var stepString = $"{" Step ".Localize()} {i + 1} {" of ".Localize()} {levelWizard.ProbeCount}:" ;
yield return new AutoProbeFeedback ( printer , this , new Vector3 ( validProbePosition , startProbeHeight ) , string . Format ( "{0} {1} {2} - {3}" , stepString , positionLabel , i + 1 , autoCalibrateLabel ) , probePositions , i , theme ) ;
}
else
{
yield return new GetCoarseBedHeight ( printer , this , new Vector3 ( validProbePosition , startProbeHeight ) , string . Format ( "{0} {1} {2} - {3}" , levelingStrings . GetStepString ( levelWizard . TotalSteps ) , positionLabel , i + 1 , lowPrecisionLabel ) , probePositions , i , levelingStrings , theme ) ;
yield return new GetFineBedHeight ( printer , this , string . Format ( "{0} {1} {2} - {3}" , levelingStrings . GetStepString ( levelWizard . TotalSteps ) , positionLabel , i + 1 , medPrecisionLabel ) , probePositions , i , levelingStrings , theme ) ;
yield return new GetUltraFineBedHeight ( printer , this , string . Format ( "{0} {1} {2} - {3}" , levelingStrings . GetStepString ( levelWizard . TotalSteps ) , positionLabel , i + 1 , highPrecisionLabel ) , probePositions , i , levelingStrings , theme ) ;
}
i + + ;
}
2018-03-29 12:31:36 -07:00
2018-04-11 17:35:34 -07:00
this . nextButton . Enabled = false ;
2018-04-10 17:59:15 -07:00
this . cancelButton . Visible = false ;
this . doneButton . Visible = true ;
2018-04-11 17:35:34 -07:00
var done1 = "Print Leveling is now configured and enabled." . Localize ( ) ;
string done2 = "If you need to recalibrate the printer in the future, the print leveling controls can be found under: Controls, Calibration" ;
string done3 = "Click 'Done' to close this window." . Localize ( ) ;
var doneString = "" ;
if ( useZProbe )
{
doneString = $"{" Congratulations ! ".Localize()} {done1}\n"
+ "\n"
+ $"{done2}\n"
+ "\n"
+ $"{done3}" ;
}
else
{
doneString = $"{" Congratulations ! ".Localize()} {done1}\n"
+ "\n"
+ $"\t• {" Remove the paper ".Localize()}\n"
+ "\n"
+ $"{done2}\n"
+ "\n"
+ $"{done3}" ;
}
yield return new LastPagelInstructions ( printer , this , "Done" . Localize ( ) , doneString , probePositions , theme ) ;
2018-04-10 17:59:15 -07:00
}
}
private Vector2 EnsureInPrintBounds ( PrinterSettings printerSettings , Vector2 probePosition )
{
// check that the position is within the printing arrea and if not move it back in
if ( printerSettings . Helpers . UseZProbe ( ) )
2018-03-29 12:31:36 -07:00
{
2018-04-10 17:59:15 -07:00
var probeOffset = printer . Settings . GetValue < Vector2 > ( SettingsKey . z_probe_xy_offset ) ;
var actualNozzlePosition = probePosition - probeOffset ;
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
// clamp this to the bed bounds
Vector2 bedSize = printer . Settings . GetValue < Vector2 > ( SettingsKey . bed_size ) ;
Vector2 printCenter = printer . Settings . GetValue < Vector2 > ( SettingsKey . print_center ) ;
RectangleDouble bedBounds = new RectangleDouble ( printCenter - bedSize / 2 , printCenter + bedSize / 2 ) ;
Vector2 adjustedPosition = bedBounds . Clamp ( actualNozzlePosition ) ;
// and push it back into the probePosition
probePosition = adjustedPosition + probeOffset ;
2017-05-19 14:39:57 -07:00
}
2018-03-29 12:31:36 -07:00
2018-04-10 17:59:15 -07:00
return probePosition ;
}
}
public abstract class LevelWizardBase : SystemWindow
{
protected PrintLevelWizardControl printLevelWizard ;
private static SystemWindow printLevelWizardWindow ;
protected PrinterConfig printer ;
2018-04-27 18:28:49 -07:00
private ThemeConfig theme ;
2018-04-10 17:59:15 -07:00
public LevelWizardBase ( PrinterConfig printer , ThemeConfig theme )
: base ( 500 , 370 )
{
AlwaysOnTopOfMain = true ;
2018-04-23 16:40:59 -07:00
this . Title = string . Format ( "{0} - {1}" , ApplicationController . Instance . ProductName , "Print Leveling Wizard" . Localize ( ) ) ;
2018-04-10 17:59:15 -07:00
2018-04-27 18:28:49 -07:00
this . theme = theme ;
this . printer = printer ;
}
public override void OnLoad ( EventArgs args )
{
2018-04-10 17:59:15 -07:00
printLevelWizard = new PrintLevelWizardControl ( printer , this , theme ) ;
AddChild ( printLevelWizard ) ;
2018-04-27 18:28:49 -07:00
base . OnLoad ( args ) ;
2018-03-29 12:31:36 -07:00
}
2018-03-29 18:12:02 -07:00
public abstract int ProbeCount { get ; }
public int TotalSteps = > ProbeCount * 3 ;
2018-03-29 12:31:36 -07:00
2018-04-09 12:22:18 -07:00
public static void ShowPrintLevelWizard ( PrinterConfig printer , ThemeConfig theme )
2015-04-08 15:20:10 -07:00
{
if ( printLevelWizardWindow = = null )
{
2018-04-03 18:17:27 -07:00
// turn off print leveling
2018-04-04 15:58:57 -07:00
PrintLevelingStream . AllowLeveling = false ;
2018-04-03 18:17:27 -07:00
2018-04-09 12:22:18 -07:00
printLevelWizardWindow = LevelWizardBase . CreateAndShowWizard ( printer , theme ) ;
2018-04-03 18:17:27 -07:00
2015-04-08 15:20:10 -07:00
printLevelWizardWindow . Closed + = ( sender , e ) = >
{
2018-04-03 18:17:27 -07:00
// If leveling was on when we started, make sure it is on when we are done.
2018-04-04 15:58:57 -07:00
PrintLevelingStream . AllowLeveling = true ;
2018-04-03 18:17:27 -07:00
2015-04-08 15:20:10 -07:00
printLevelWizardWindow = null ;
2017-06-07 15:56:02 -07:00
2018-03-29 18:12:02 -07:00
// make sure we raise the probe on close
2017-09-15 12:08:00 -07:00
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 ) )
2017-06-07 15:56:02 -07:00
{
// make sure the servo is retracted
2017-09-15 12:08:00 -07:00
var servoRetract = printer . Settings . GetValue < double > ( SettingsKey . z_servo_retracted_angle ) ;
2018-01-04 17:30:48 -08:00
printer . Connection . QueueLine ( $"M280 P0 S{servoRetract}" ) ;
2017-06-07 15:56:02 -07:00
}
2015-04-08 15:20:10 -07:00
} ;
}
else
{
printLevelWizardWindow . BringToFront ( ) ;
}
}
2018-04-28 16:59:21 -07:00
public IEnumerable < Vector2 > GetSampleRing ( int numberOfSamples , double ratio , double phase )
{
double bedRadius = Math . Min ( printer . Settings . GetValue < Vector2 > ( SettingsKey . bed_size ) . X , printer . Settings . GetValue < Vector2 > ( SettingsKey . bed_size ) . Y ) / 2 ;
Vector2 bedCenter = printer . Settings . GetValue < Vector2 > ( SettingsKey . print_center ) ;
for ( int i = 0 ; i < numberOfSamples ; i + + )
{
Vector2 position = new Vector2 ( bedRadius * ratio , 0 ) ;
position . Rotate ( MathHelper . Tau / numberOfSamples * i + phase ) ;
position + = bedCenter ;
yield return position ;
}
}
2018-03-29 18:12:02 -07:00
public abstract IEnumerable < Vector2 > GetPrintLevelPositionToSample ( ) ;
2018-04-09 12:22:18 -07:00
private static LevelWizardBase CreateAndShowWizard ( PrinterConfig printer , ThemeConfig theme )
2015-04-08 15:20:10 -07:00
{
2016-10-11 14:56:36 -07:00
// clear any data that we are going to be acquiring (sampled positions, after z home offset)
2018-04-03 18:17:27 -07:00
PrintLevelingData levelingData = new PrintLevelingData ( )
{
LevelingSystem = printer . Settings . GetValue < LevelingSystem > ( SettingsKey . print_leveling_solution )
} ;
2017-09-15 12:08:00 -07:00
printer . Settings . SetValue ( SettingsKey . baby_step_z_offset , "0" ) ;
2017-02-22 16:16:40 -08:00
2015-04-08 15:20:10 -07:00
LevelWizardBase printLevelWizardWindow ;
2018-03-30 14:48:55 -07:00
switch ( levelingData . LevelingSystem )
2015-04-08 15:20:10 -07:00
{
2018-03-30 14:48:55 -07:00
case LevelingSystem . Probe3Points :
2018-04-09 12:22:18 -07:00
printLevelWizardWindow = new LevelWizard3Point ( printer , theme ) ;
2015-04-08 15:20:10 -07:00
break ;
2018-03-30 14:48:55 -07:00
case LevelingSystem . Probe7PointRadial :
2018-04-09 12:22:18 -07:00
printLevelWizardWindow = new LevelWizard7PointRadial ( printer , theme ) ;
2015-08-01 14:44:53 -07:00
break ;
2018-03-30 14:48:55 -07:00
case LevelingSystem . Probe13PointRadial :
2018-04-09 12:22:18 -07:00
printLevelWizardWindow = new LevelWizard13PointRadial ( printer , theme ) ;
2015-08-05 11:12:01 -07:00
break ;
2018-04-28 16:59:21 -07:00
case LevelingSystem . Probe100PointRadial :
printLevelWizardWindow = new LevelWizard100PointRadial ( printer , theme ) ;
break ;
2018-03-30 14:48:55 -07:00
case LevelingSystem . Probe3x3Mesh :
2018-04-27 18:28:49 -07:00
printLevelWizardWindow = new LevelWizardMesh ( printer , 3 , 3 , theme ) ;
2017-05-19 14:39:57 -07:00
break ;
2018-04-04 13:45:10 -07:00
case LevelingSystem . Probe5x5Mesh :
2018-04-27 18:28:49 -07:00
printLevelWizardWindow = new LevelWizardMesh ( printer , 5 , 5 , theme ) ;
break ;
case LevelingSystem . Probe10x10Mesh :
printLevelWizardWindow = new LevelWizardMesh ( printer , 10 , 10 , theme ) ;
2018-04-04 13:45:10 -07:00
break ;
2015-04-08 15:20:10 -07:00
default :
throw new NotImplementedException ( ) ;
}
printLevelWizardWindow . ShowAsSystemWindow ( ) ;
return printLevelWizardWindow ;
}
}
2018-03-29 18:12:02 -07:00
// this class is so that it is not passed by value
public class ProbePosition
2015-04-08 15:20:10 -07:00
{
2018-03-29 18:12:02 -07:00
public Vector3 position ;
2015-04-08 15:20:10 -07:00
}
}