Add gradientspace gsSlicer

This commit is contained in:
jlewin 2019-06-27 18:54:01 -07:00
parent 9523ae320f
commit 03cc686292
15 changed files with 1861 additions and 2 deletions

11
.gitmodules vendored
View file

@ -3,4 +3,13 @@
url = https://github.com/MatterHackers/MatterSlice.git
[submodule "Submodules/agg-sharp"]
path = Submodules/agg-sharp
url = https://github.com/MatterHackers/agg-sharp.git
url = https://github.com/MatterHackers/agg-sharp.git
[submodule "Submodules/geometry3Sharp"]
path = Submodules/geometry3Sharp
url = https://github.com/MatterHackers/geometry3Sharp
[submodule "Submodules/gsGCode"]
path = Submodules/gsGCode
url = https://github.com/MatterHackers/gsGCode
[submodule "Submodules/gsSlicer"]
path = Submodules/gsSlicer
url = https://github.com/MatterHackers/gsSlicer

View file

@ -0,0 +1,158 @@
/*
Copyright (c) 2019, 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.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using cotangent;
using g3;
using gs;
using MatterHackers.Agg;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
namespace MatterHackers.gsBundle
{
public class BasicSlicer : IObjectSlicer
{
public async Task<bool> Slice(IObject3D object3D, IEnumerable<IObject3D> printableItems, PrinterSettings printerSettings, string filePath, IProgress<ProgressStatus> progressReporter, CancellationToken cancellationToken)
{
using (var outputStream = File.OpenWrite(filePath))
{
var sourceMeshes = new List<DMesh3>();
foreach (var item in object3D.VisibleMeshes().Where(d => d.MeshPath != null))
{
string sourceFilePath = await item.ResolveFilePath(null, cancellationToken);
// Load Mesh
if (File.Exists(sourceFilePath))
{
var mesh = StandardMeshReader.ReadMesh(sourceFilePath);
if (mesh != null)
{
sourceMeshes.Add(mesh);
}
var printCenter = printerSettings.GetValue<Vector2>(SettingsKey.print_center);
ApplyTransform(mesh, item.WorldMatrix(), printCenter);
}
}
PrintSettings settings = LoadSettingsForPrinter(printerSettings);
// Construct slicer
var slicer = new GeometrySlicer();
slicer.SliceMeshes(sourceMeshes, settings);
bool valid = slicer.ExtractResultsIfValid(out PrintMeshAssembly meshes, out PlanarSliceStack slices);
// Construct GCode generator
var pathGenerator = new ToolpathGenerator();
pathGenerator.CreateToolPaths(meshes, slices, settings);
// Write GCode file
var gcodeWriter = new StandardGCodeWriter();
var streamWriter = new StreamWriter(outputStream);
gcodeWriter.WriteFile(pathGenerator.CurrentGCode, streamWriter);
return true;
}
}
private static PrintSettings LoadSettingsForPrinter(PrinterSettings printerSettings)
{
var ss = new MatterHackersPrinter(printerSettings);
var settings = new PrintSettings();
settings.UpdateFromSettings(ss);
return settings;
}
// Modeled after FlipLeftRightCoordSystems and ConvertYUpToZUp examples
public static void ApplyTransform(IDeformableMesh mesh, Matrix4X4 matrix, Vector2 printCenter)
{
int NV = mesh.MaxVertexID;
for (int vid = 0; vid < NV; ++vid)
{
if (mesh.IsVertex(vid))
{
Vector3d v = mesh.GetVertex(vid);
// Transform point to MatterControl bed translation
var vec3 = new Vector3(v.x, v.y, v.z);
var transformed = vec3.Transform(matrix);
// Update and reset
v.x = transformed.X - printCenter.X;
v.y = transformed.Y - printCenter.Y;
v.z = transformed.Z;
mesh.SetVertex(vid, v);
}
}
}
public Dictionary<string, ExportField> Exports { get; } = new Dictionary<string, ExportField>()
{
[SettingsKey.external_perimeter_speed] = new ExportField(""),
[SettingsKey.make] = new ExportField(""),
[SettingsKey.model] = new ExportField(""),
[SettingsKey.build_height] = new ExportField(""),
[SettingsKey.nozzle_diameter] = new ExportField(""),
[SettingsKey.filament_diameter] = new ExportField(""),
[SettingsKey.has_heated_bed] = new ExportField(""),
[SettingsKey.has_hardware_leveling] = new ExportField(""),
[SettingsKey.layer_height] = new ExportField(""),
[SettingsKey.temperature1] = new ExportField(""),
[SettingsKey.bed_temperature] = new ExportField(""),
[SettingsKey.retract_length] = new ExportField(""),
[SettingsKey.retract_speed] = new ExportField(""),
[SettingsKey.travel_speed] = new ExportField(""),
[SettingsKey.first_layer_speed] = new ExportField(""),
[SettingsKey.bottom_solid_layers] = new ExportField(""),
[SettingsKey.top_solid_layers] = new ExportField("")
};
public bool ValidateFile(string filePath)
{
// TODO: Implement solution
System.Diagnostics.Debugger.Break();
return true;
}
}
}

View file

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>F:\Sources\mccentral\MatterControl\bin\Debug</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MatterControl.Common\MatterControl.Common.csproj" />
<ProjectReference Include="..\MatterControl.Printing\MatterControl.Printing.csproj" />
<ProjectReference Include="..\Submodules\agg-sharp\agg\Agg.csproj" />
<ProjectReference Include="..\Submodules\agg-sharp\DataConverters3D\DataConverters3D.csproj" />
<ProjectReference Include="..\Submodules\agg-sharp\VectorMath\VectorMath.csproj" />
<ProjectReference Include="..\Submodules\geometry3Sharp\geometry3Sharp.csproj" />
<ProjectReference Include="..\Submodules\gsGCode\gsGCode.csproj" />
<ProjectReference Include="..\Submodules\gsGeometry\gsGeometry.csproj" />
<ProjectReference Include="..\Submodules\gsSlicerPro\gsSlicerPro.csproj" />
<ProjectReference Include="..\Submodules\gsSlicer\gsSlicer.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,129 @@
/*
Copyright (c) 2019, 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 gs;
using MatterHackers.Agg;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
namespace MatterHackers.gsBundle
{
public class MatterHackersPrinter : GenericRepRapSettings
{
public MatterHackersPrinter(PrinterSettings settings)
{
string make = settings.GetValue<string>(SettingsKey.make);
string model = settings.GetValue<string>(SettingsKey.model);
var printCenter = settings.GetValue<Vector2>(SettingsKey.print_center);
var bedSize = settings.GetValue<Vector2>(SettingsKey.bed_size);
machineInfo = new FFFMachineInfo()
{
ManufacturerName = make,
ManufacturerUUID = agg_basics.GetLongHashCode(make).ToString(),
ModelIdentifier = model,
ModelUUID = agg_basics.GetLongHashCode(model).ToString(),
Class = MachineClass.PlasticFFFPrinter,
BedSizeXMM = (int)settings.BedBounds.Width,
BedSizeYMM = (int)settings.BedBounds.Height,
// BedSizeZMM = settings.GetValue<int>(SettingsKey.build_height),
MaxHeightMM = settings.GetValue<int>(SettingsKey.build_height),
NozzleDiamMM = settings.GetValue<double>(SettingsKey.nozzle_diameter),
FilamentDiamMM = settings.GetValue<double>(SettingsKey.filament_diameter),
// Set bed origin factors based on printer center/bedsize
BedOriginFactorX = printCenter.X / bedSize.X,
BedOriginFactorY = printCenter.Y / bedSize.Y,
HasHeatedBed = settings.GetValue<bool>(SettingsKey.has_heated_bed),
HasAutoBedLeveling = settings.GetValue<bool>(SettingsKey.has_hardware_leveling),
EnableAutoBedLeveling = false,
// TODO: Consider how to adapt and/or update for MatterControl printers
MaxExtruderTempC = 250,
MaxBedTempC = 80,
MaxExtrudeSpeedMMM = 80 * 60,
MaxTravelSpeedMMM = 120 * 60,
MaxZTravelSpeedMMM = 100 * 60,
MaxRetractSpeedMMM = 45 * 60,
MinLayerHeightMM = 0.05,
MaxLayerHeightMM = 0.3,
};
LayerHeightMM = settings.GetValue<double>(SettingsKey.layer_height);
ExtruderTempC = (int)settings.GetValue<double>(SettingsKey.temperature1);
HeatedBedTempC = (int)settings.GetValue<double>(SettingsKey.bed_temperature);
RoofLayers = settings.GetValue<int>(SettingsKey.top_solid_layers);
FloorLayers = settings.GetValue<int>(SettingsKey.bottom_solid_layers);
SolidFillNozzleDiamStepX = 1.0;
RetractDistanceMM = settings.GetValue<double>(SettingsKey.retract_length);
RetractSpeed = settings.GetValue<double>(SettingsKey.retract_speed) * 60;
ZTravelSpeed = settings.Helpers.GetMovementSpeeds()["z"] * 60;
RapidTravelSpeed = settings.GetValue<double>(SettingsKey.travel_speed) * 60;
CarefulExtrudeSpeed = settings.GetValue<double>(SettingsKey.first_layer_speed) * 60;
RapidExtrudeSpeed = Machine.MaxExtrudeSpeedMMM;
// Looks like fractional of perimeter speed
var outerSpeed = settings.GetValue<double>(SettingsKey.external_perimeter_speed);
var innerSpeed = settings.GetValue<double>(SettingsKey.external_perimeter_speed);
OuterPerimeterSpeedX = outerSpeed / innerSpeed;
}
public override AssemblerFactoryF AssemblerType()
{
return (GCodeBuilder builder, SingleMaterialFFFSettings settings) =>
{
return new RepRapAssembler(builder, settings)
{
HeaderCustomizerF = HeaderCustomF
};
};
}
protected void HeaderCustomF(RepRapAssembler.HeaderState state, GCodeBuilder Builder)
{
if (state == RepRapAssembler.HeaderState.BeforePrime)
{
if (Machine.HasAutoBedLeveling && Machine.EnableAutoBedLeveling)
{
Builder.BeginGLine(29, "auto-level bed");
}
}
}
}
}

View file

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace cotangent
{
public enum LineWidthType
{
World,
Pixel
}
public enum FrameType
{
LocalFrame = 0,
WorldFrame = 1
};
public enum UpDirection
{
ZUp = 0,
YUp = 1
}
public enum PivotLocation
{
Center = 0,
BaseCenter = 1
}
// will/may be called per-frame to give a chance to do something with shortcut keys
// return true to indicate that key was handled, ie "capture" it
public interface IShortcutKeyHandler
{
bool HandleShortcuts();
}
public interface ITextEntryTarget
{
bool ConsumeAllInput();
bool OnBeginTextEntry();
bool OnEndTextEntry();
bool OnBackspace();
bool OnDelete();
bool OnReturn();
bool OnEscape();
bool OnLeftArrow();
bool OnRightArrow();
bool OnCharacters(string s);
}
public enum CameraInteractionState
{
BeginCameraAction,
EndCameraAction,
Ignore
}
}

View file

@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using g3;
using gs;
using gs.info;
namespace cotangent
{
/// <summary>
/// Auto-compute slicing of current CC.PrintMeshes
///
/// [TODO]
/// - be smarter about polylines?
/// - don't necessarily always need to discard SlicerMeshes, for example if only
/// slicing params changed, we can avoid copy (maybe irrelevant though)
/// </summary>
public class GeometrySlicer
{
PrintMeshAssembly SlicerMeshes;
PlanarSliceStack SliceSet;
bool SliceStackValid;
object data_lock = new object();
bool show_slice_polylines = false;
public bool ShowSlicePolylines
{
get { return show_slice_polylines; }
set { set_slice_polylines_visible(value); }
}
public delegate void SlicingStateChangeHandler();
public event SlicingStateChangeHandler SlicingInvalidatedEvent;
public event SlicingStateChangeHandler SlicingUpdatedEvent;
// public event SlicingProgressHandler SlicingProgressEvent;
public bool PauseSlicing = false;
public GeometrySlicer()
{
InvalidateSlicing();
}
public bool IsResultValid()
{
bool valid = false;
lock (data_lock)
{
valid = SliceStackValid;
}
return valid;
}
public bool ExtractResultsIfValid(out PrintMeshAssembly printMeshes, out PlanarSliceStack slices)
{
bool valid = false;
lock (data_lock)
{
printMeshes = SlicerMeshes;
slices = SliceSet;
valid = SliceStackValid;
}
return valid;
}
public void InvalidateSlicing()
{
//cancel_active_compute();
lock (data_lock)
{
SlicerMeshes = null;
SliceSet = null;
SliceStackValid = false;
}
// discard_slice_polylines();
}
public void SliceMeshes(List<DMesh3> sourceMeshes, PrintSettings printSettings)
{
SliceStackValid = false;
try
{
var sliceTask = new SliceTask();
sliceTask.meshCopies = sourceMeshes;
sliceTask.Compute(printSettings);
SlicerMeshes = sliceTask.MeshAssembly;
SliceSet = sliceTask.SliceStack;
SliceStackValid = true;
}
catch (Exception ex)
{
}
}
float last_spawn_time = 0;
SliceTask active_compute;
object active_compute_lock = new object();
class SliceTask
{
// input data
public Frame3f[] meshToScene;
public Vector3f[] localScale;
public List<DMesh3> meshCopies { get; set; }
public PrintMeshSettings[] meshSettings;
// computed data
public bool Finished;
public bool Success;
public PrintMeshAssembly MeshAssembly;
public PlanarSliceStack SliceStack;
// internal
MeshPlanarSlicerPro slicer;
public SliceTask()
{
Finished = false;
Success = false;
}
public void Compute(PrintSettings printSettings)
{
int N = meshCopies.Count();
slicer = new MeshPlanarSlicerPro()
{
LayerHeightMM = printSettings.LayerHeightMM,
// [RMS] 1.5 here is a hack. If we don't leave a bit of space then often the filament gets squeezed right at
// inside/outside transitions, which is bad. Need a better way to handle.
OpenPathDefaultWidthMM = printSettings.NozzleDiameterMM * 1.5,
SetMinZValue = 0,
SliceFactoryF = PlanarSlicePro.FactoryF
};
if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Clipped)
slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Clipped;
else if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Embedded)
slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Embedded;
else if (printSettings.OpenMode == PrintSettings.OpenMeshMode.Ignored)
slicer.DefaultOpenPathMode = PrintMeshOptions.OpenPathsModes.Ignored;
if (printSettings.StartLayers > 0)
{
int start_layers = printSettings.StartLayers;
double std_layer_height = printSettings.LayerHeightMM;
double start_layer_height = printSettings.StartLayerHeightMM;
slicer.LayerHeightF = (layer_i) =>
{
return (layer_i < start_layers) ? start_layer_height : std_layer_height;
};
}
try
{
MeshAssembly = new PrintMeshAssembly();
for (int k = 0; k < N; ++k)
{
DMesh3 mesh = meshCopies[k];
//Frame3f mapF = meshToScene[k];
PrintMeshSettings settings = new PrintMeshSettings();
var options = new PrintMeshOptions
{
IsSupport = (settings.ObjectType == PrintMeshSettings.ObjectTypes.Support),
IsCavity = (settings.ObjectType == PrintMeshSettings.ObjectTypes.Cavity),
IsCropRegion = (settings.ObjectType == PrintMeshSettings.ObjectTypes.CropRegion),
IsOpen = settings.OuterShellOnly,
OpenPathMode = PrintMeshSettings.Convert(settings.OpenMeshMode),
Extended = new ExtendedPrintMeshOptions()
{
ClearanceXY = settings.Clearance,
OffsetXY = settings.OffsetXY
}
};
//Vector3f scale = localScale[k];
//MeshTransforms.Scale(mesh, scale.x, scale.y, scale.z);
//MeshTransforms.FromFrame(mesh, mapF);
//MeshTransforms.FlipLeftRightCoordSystems(mesh);
//MeshTransforms.ConvertYUpToZUp(mesh);
var decomposer = new MeshAssembly(mesh)
{
HasNoVoids = settings.NoVoids
};
decomposer.Decompose();
MeshAssembly.AddMeshes(decomposer.ClosedSolids, options);
PrintMeshOptions openOptions = options.Clone();
MeshAssembly.AddMeshes(decomposer.OpenMeshes, openOptions);
}
if (slicer.Add(MeshAssembly) == false)
throw new Exception("error adding PrintMeshAssembly to Slicer!!");
// set clip box
Box2d clip_box = new Box2d(
Vector2d.Zero,
new Vector2d(printSettings.BedSizeXMM / 2, printSettings.BedSizeYMM / 2));
slicer.ValidRegions = new List<GeneralPolygon2d>() {
new GeneralPolygon2d(new Polygon2d(clip_box.ComputeVertices()))
};
SliceStack = slicer.Compute();
Success = true;
}
catch (Exception e)
{
//DebugUtil.Log("GeometrySlicer.Compute: exception: " + e.Message);
System.Diagnostics.Debugger.Break();
Success = false;
}
Finished = true;
}
}
void set_slice_polylines_visible(bool bSet)
{
if (bSet == show_slice_polylines)
return;
compute_slice_polylines();
show_slice_polylines = true;
}
void compute_slice_polylines()
{
// fMaterial mat1 = MaterialUtil.CreateFlatMaterialF(Colorf.Black);
// fMaterial mat2 = MaterialUtil.CreateFlatMaterialF(Colorf.BlueMetal);
// [TODO] do we need to hold data_lock here? seems like no since main thread is blocked,
// then it would never be the case that we are setting SliceSet = null
// create geometry
int slice_i = 0;
//SlicePolylines = new List<fPolylineGameObject>();
foreach (PlanarSlice slice in SliceSet.Slices)
{
//DebugUtil.Log(2, "Slice has {0} solids", slice.Solids.Count);
Colorf slice_color = (slice_i % 2 == 0) ? Colorf.Black : Colorf.BlueMetal;
// fMaterial slice_mat = (slice_i % 2 == 0) ? mat1 : mat2;
slice_i++;
foreach (GeneralPolygon2d poly in slice.Solids)
{
List<Vector3f> polyLine = new List<Vector3f>();
for (int pi = 0; pi <= poly.Outer.VertexCount; ++pi)
{
int i = pi % poly.Outer.VertexCount;
Vector2d v2 = poly.Outer[i];
Vector2d n2 = poly.Outer.GetTangent(i).Perp;
Vector3d v3 = new Vector3d(v2.x, v2.y, slice.Z);
v3 = MeshTransforms.ConvertZUpToYUp(v3);
v3 = MeshTransforms.FlipLeftRightCoordSystems(v3);
Vector3d n3 = MeshTransforms.ConvertZUpToYUp(new Vector3d(n2.x, n2.y, 0));
n3 = MeshTransforms.FlipLeftRightCoordSystems(n3);
n3.Normalize();
v3 += 0.1f * n3;
polyLine.Add((Vector3f)v3);
}
// Do something with polyline....
Console.WriteLine(polyLine);
////DebugUtil.Log(2, "Polyline has {0} vertiecs", polyLine.Count);
//fPolylineGameObject go = GameObjectFactory.CreatePolylineGO(
// "slice_outer", polyLine, slice_color, 0.1f, LineWidthType.World);
//go.SetMaterial(slice_mat, true);
//CC.ActiveScene.RootGameObject.AddChild(go, false);
//SlicePolylines.Add(go);
}
}
}
}
}

View file

@ -0,0 +1,74 @@
using System;
namespace cotangent
{
public static class DebugUtil
{
internal static void Log(string v)
{
Console.WriteLine(v);
//throw new NotImplementedException();
}
internal static void Log(int v1, string v2)
{
Console.WriteLine("{0} - {1}", v1, v2);
}
internal static void Log(int v1, string v2, string msg, string trace)
{
Console.WriteLine("{0} - {1} {2}", v1, v2, msg);
}
}
public class PrintMeshSettings
{
// WARNING: this class is serialized! Do not change enum constants!
public int Version = 1;
public enum ObjectTypes
{
Solid = 0, Support = 1, Cavity = 2, CropRegion = 3, Ignored = 4
}
public enum OpenMeshModes
{
Default = 0, Clipped = 1, Embedded = 2, Ignored = 3,
}
public ObjectTypes ObjectType = ObjectTypes.Solid;
public bool NoVoids = false;
public bool OuterShellOnly = false;
public OpenMeshModes OpenMeshMode = OpenMeshModes.Default;
public double Clearance = 0;
public double OffsetXY = 0;
public PrintMeshSettings Clone() {
return new PrintMeshSettings() {
ObjectType = this.ObjectType,
NoVoids = this.NoVoids,
OuterShellOnly = this.OuterShellOnly,
OpenMeshMode = this.OpenMeshMode,
Clearance = this.Clearance,
OffsetXY = this.OffsetXY
};
}
public static gs.PrintMeshOptions.OpenPathsModes Convert(OpenMeshModes mode)
{
switch (mode) {
case OpenMeshModes.Clipped: return gs.PrintMeshOptions.OpenPathsModes.Clipped;
case OpenMeshModes.Embedded: return gs.PrintMeshOptions.OpenPathsModes.Embedded;
case OpenMeshModes.Ignored: return gs.PrintMeshOptions.OpenPathsModes.Ignored;
default:
case OpenMeshModes.Default: return gs.PrintMeshOptions.OpenPathsModes.Default;
}
}
}
}

View file

@ -0,0 +1,462 @@

using System;
using gs;
namespace cotangent
{
public class PrintSettings
{
protected SingleMaterialFFFSettings Active;
protected bool bValueModified = false;
public bool HasModifiedValues
{
get { return bValueModified; }
}
protected double layer_height = 0.2;
public double LayerHeightMM
{
get { return layer_height; }
set { if (layer_height != value) { layer_height = value; setting_modified(true, true); } }
}
public bool LayerHeightMM_Modified { get { return layer_height != Active.LayerHeightMM; } }
public enum OpenMeshMode
{
Clipped = 0, Embedded = 1, Ignored = 2
}
OpenMeshMode open_mode = OpenMeshMode.Clipped;
public OpenMeshMode OpenMode
{
get { return open_mode; }
}
public int OpenModeInt
{
get { return (int)open_mode; }
set
{
OpenMeshMode newmode = (OpenMeshMode)value;
if (open_mode != newmode)
{
open_mode = newmode; setting_modified(true, true);
}
}
}
protected int outer_shells = 2;
public int OuterShells
{
get { return outer_shells; }
set { if (outer_shells != value) { outer_shells = value; setting_modified(false, true); } }
}
public bool OuterShells_Modified { get { return outer_shells != Active.Shells; } }
protected int roof_layers = 2;
public int RoofLayers
{
get { return roof_layers; }
set { if (roof_layers != value) { roof_layers = value; setting_modified(false, true); } }
}
public bool RoofLayers_Modified { get { return roof_layers != Active.RoofLayers; } }
protected int floor_layers = 2;
public int FloorLayers
{
get { return floor_layers; }
set { if (floor_layers != value) { floor_layers = value; setting_modified(false, true); } }
}
public bool FloorLayers_Modified { get { return floor_layers != Active.FloorLayers; } }
protected double infill_step = 3.0;
public double InfillStepX
{
get { return infill_step; }
set { if (infill_step != value) { infill_step = value; setting_modified(false, true); } }
}
public bool InfillStepX_Modified { get { return infill_step != Active.SparseLinearInfillStepX; } }
protected int interior_solid_region_shells = 0;
public int InteriorSolidRegionShells
{
get { return interior_solid_region_shells; }
set { if (interior_solid_region_shells != value) { interior_solid_region_shells = value; setting_modified(false, true); } }
}
public bool InteriorSolidRegionShells_Modified { get { return interior_solid_region_shells != Active.InteriorSolidRegionShells; } }
protected bool clip_self_overlaps = false;
public bool ClipSelfOverlaps
{
get { return clip_self_overlaps; }
set { if (clip_self_overlaps != value) { clip_self_overlaps = value; setting_modified(false, true); } }
}
public bool ClipSelfOverlaps_Modified { get { return clip_self_overlaps != Active.ClipSelfOverlaps; } }
/*
* Start layer settings
*/
protected int start_layers = 0;
public int StartLayers
{
get { return start_layers; }
set { if (start_layers != value) { start_layers = value; setting_modified(true, true); } }
}
public bool StartLayers_Modified { get { return start_layers != Active.StartLayers; } }
protected double start_layer_height = 0.2;
public double StartLayerHeightMM
{
get { return start_layer_height; }
set { if (start_layer_height != value) { start_layer_height = value; setting_modified(true, true); } }
}
public bool StartLayerHeightMM_Modified { get { return start_layer_height != Active.StartLayerHeightMM; } }
/*
* Support settings
*/
protected bool generate_support = false;
public bool GenerateSupport
{
get { return generate_support; }
set { if (generate_support != value) { generate_support = value; setting_modified(true, true); } }
}
public bool GenerateSupport_Modified { get { return generate_support != Active.GenerateSupport; } }
protected double overhang_angle = 35.0;
public double OverhangAngleDeg
{
get { return overhang_angle; }
set { if (overhang_angle != value) { overhang_angle = value; setting_modified(false, true); } }
}
public bool OverhangAngleDeg_Modified { get { return overhang_angle != Active.SupportOverhangAngleDeg; } }
protected bool support_minz_tips = true;
public bool SupportMinZTips
{
get { return support_minz_tips; }
set { if (support_minz_tips != value) { support_minz_tips = value; setting_modified(false, true); } }
}
public bool SupportMinZTips_Modified { get { return support_minz_tips != Active.SupportMinZTips; } }
protected double support_step = 3.0;
public double SupportStepX
{
get { return support_step; }
set { if (support_step != value) { support_step = value; setting_modified(false, true); } }
}
public bool SupportStepX_Modified { get { return support_step != Active.SupportSpacingStepX; } }
protected bool enable_support_shell = false;
public bool EnableSupportShell
{
get { return enable_support_shell; }
set { if (enable_support_shell != value) { enable_support_shell = value; setting_modified(false, true); } }
}
public bool EnableSupportShell_Modified { get { return enable_support_shell != Active.EnableSupportShell; } }
protected bool enable_support_release_opt = false;
public bool EnableSupportReleaseOpt
{
get { return enable_support_release_opt; }
set { if (enable_support_release_opt != value) { enable_support_release_opt = value; setting_modified(false, true); } }
}
public bool EnableSupportReleaseOpt_Modified { get { return enable_support_release_opt != Active.EnableSupportReleaseOpt; } }
protected double support_solid_space = 0.3;
public double SupportSolidSpace
{
get { return support_solid_space; }
set { if (support_solid_space != value) { support_solid_space = value; setting_modified(false, true); } }
}
public bool SupportSolidSpace_Modified { get { return support_solid_space != Active.SupportSolidSpace; } }
/*
* Bridging settings
*/
protected bool enable_bridging = false;
public bool EnableBridging
{
get { return enable_bridging; }
set { if (enable_bridging != value) { enable_bridging = value; setting_modified(false, true); } }
}
public bool EnableBridging_Modified { get { return enable_bridging != Active.EnableBridging; } }
protected double max_bridge_dist = 10.0;
public double MaxBridgeDistanceMM
{
get { return max_bridge_dist; }
set { if (max_bridge_dist != value) { max_bridge_dist = value; setting_modified(false, true); } }
}
public bool MaxBridgeDistanceMM_Modified { get { return max_bridge_dist != Active.MaxBridgeWidthMM; } }
/*
* Extra settings
*/
protected int layer_range_min = 1;
public int LayerRangeMin
{
get { return layer_range_min; }
set { if (layer_range_min != value) { layer_range_min = value; setting_modified(false, true); } }
}
public bool LayerRangeMin_Modified { get { return layer_range_min != (Active.LayerRangeFilter.a + 1); } }
protected int layer_range_max = 999999999;
public int LayerRangeMax
{
get { return layer_range_max; }
set { if (layer_range_max != value) { layer_range_max = value; setting_modified(false, true); } }
}
public bool LayerRangeMax_Modified { get { return layer_range_max != (Active.LayerRangeFilter.b + 1); } }
/*
* Machine settings
*/
protected double nozzle_diam = 0.4;
public double NozzleDiameterMM
{
get { return nozzle_diam; }
set { if (nozzle_diam != value) { nozzle_diam = value; setting_modified(true, true); } }
}
public bool NozzleDiameterMM_Modified { get { return nozzle_diam != Active.Machine.NozzleDiamMM; } }
protected double filament_diam = 0.4;
public double FilamentDiameterMM
{
get { return filament_diam; }
set { if (filament_diam != value) { filament_diam = value; setting_modified(true, true); } }
}
public bool FilamentDiameterMM_Modified { get { return filament_diam != Active.Machine.FilamentDiamMM; } }
protected int extruder_temp = 230;
public int ExtruderTempC
{
get { return extruder_temp; }
set { if (extruder_temp != value) { extruder_temp = value; setting_modified(false, true); } }
}
public bool ExtruderTempC_Modified { get { return extruder_temp != Active.ExtruderTempC; } }
public bool HasHeatedBed
{
get { return Active.Machine.HasHeatedBed; }
}
protected int bed_temp = 0;
public int BedTempC
{
get { return bed_temp; }
set { if (bed_temp != value) { bed_temp = value; setting_modified(false, true); } }
}
public bool BedTempC_Modified { get { return bed_temp != Active.HeatedBedTempC; } }
protected int print_speed = 90;
public int PrintSpeedMMS
{
get { return print_speed; }
set { if (print_speed != value) { print_speed = value; setting_modified(false, true); } }
}
public bool PrintSpeedMMS_Modified { get { return print_speed != (int)Math.Round(Active.RapidExtrudeSpeed / 60, 0); } }
protected int travel_speed = 150;
public int TravelSpeedMMS
{
get { return travel_speed; }
set { if (travel_speed != value) { travel_speed = value; setting_modified(false, true); } }
}
public bool TravelSpeedMMS_Modified { get { return travel_speed != (int)Math.Round(Active.RapidTravelSpeed / 60, 0); } }
protected int fan_speed_x = 100;
public int FanSpeedX
{
get { return fan_speed_x; }
set { if (fan_speed_x != value) { fan_speed_x = value; setting_modified(false, true); } }
}
public bool FanSpeedX_Modified { get { return fan_speed_x != (int)Math.Round(Active.FanSpeedX * 100, 0); } }
protected int bed_size_x = 100;
public int BedSizeXMM
{
get { return bed_size_x; }
set { if (bed_size_x != value) { bed_size_x = value; setting_modified(false, true); } }
}
public bool BedSizeXMM_Modified { get { return bed_size_x != (int)Active.Machine.BedSizeXMM; } }
protected int bed_size_y = 100;
public int BedSizeYMM
{
get { return bed_size_y; }
set { if (bed_size_y != value) { bed_size_y = value; setting_modified(false, true); } }
}
public bool BedSizeYMM_Modified { get { return bed_size_y != (int)Active.Machine.BedSizeYMM; } }
protected int bed_size_z = 100;
public int BedSizeZMM
{
get { return bed_size_z; }
set { if (bed_size_z != value) { bed_size_z = value; setting_modified(false, true); } }
}
public bool BedSizeZMM_Modified { get { return bed_size_z != (int)Active.Machine.MaxHeightMM; } }
public delegate void SettingsModifiedEvent(PrintSettings settings);
public event SettingsModifiedEvent OnNewSettings;
public event SettingsModifiedEvent OnSettingModified;
void setting_modified(bool invalidate_slicing, bool invalidate_paths)
{
//if (invalidate_slicing)
// CC.InvalidateSlicing();
//else if (invalidate_paths)
// CC.InvalidateToolPaths();
bValueModified = true;
OnSettingModified?.Invoke(this);
}
public void UpdateFromSettings(PlanarAdditiveSettings settings)
{
if (settings == null)
return;
if (settings is SingleMaterialFFFSettings == false)
throw new Exception("PrintSettings.UpdateFromSettings: invalid settings type!");
SingleMaterialFFFSettings ss = settings as SingleMaterialFFFSettings;
Active = ss;
LayerHeightMM = ss.LayerHeightMM;
OuterShells = ss.Shells;
RoofLayers = ss.RoofLayers;
FloorLayers = ss.FloorLayers;
InfillStepX = ss.SparseLinearInfillStepX;
ClipSelfOverlaps = ss.ClipSelfOverlaps;
InteriorSolidRegionShells = ss.InteriorSolidRegionShells;
StartLayers = ss.StartLayers;
StartLayerHeightMM = ss.StartLayerHeightMM;
GenerateSupport = ss.GenerateSupport;
OverhangAngleDeg = ss.SupportOverhangAngleDeg;
SupportMinZTips = ss.SupportMinZTips;
EnableSupportShell = ss.EnableSupportShell;
EnableSupportReleaseOpt = ss.EnableSupportReleaseOpt;
SupportStepX = ss.SupportSpacingStepX;
SupportSolidSpace = ss.SupportSolidSpace;
EnableBridging = ss.EnableBridging;
MaxBridgeDistanceMM = ss.MaxBridgeWidthMM;
LayerRangeMin = ss.LayerRangeFilter.a + 1;
LayerRangeMax = ss.LayerRangeFilter.b + 1;
NozzleDiameterMM = ss.Machine.NozzleDiamMM;
FilamentDiameterMM = ss.Machine.FilamentDiamMM;
ExtruderTempC = ss.ExtruderTempC;
BedTempC = ss.HeatedBedTempC;
PrintSpeedMMS = (int)Math.Round(ss.RapidExtrudeSpeed / 60, 0);
TravelSpeedMMS = (int)Math.Round(ss.RapidTravelSpeed / 60, 0);
FanSpeedX = (int)Math.Round(ss.FanSpeedX * 100, 0);
BedSizeXMM = (int)ss.Machine.BedSizeXMM;
BedSizeYMM = (int)ss.Machine.BedSizeYMM;
BedSizeZMM = (int)ss.Machine.MaxHeightMM;
bValueModified = false;
OnNewSettings?.Invoke(this);
}
public void WriteToSettings(PlanarAdditiveSettings settings)
{
if (settings is SingleMaterialFFFSettings == false)
throw new Exception("PrintSettings.UpdateToSettings: invalid settings type!");
WriteToSettings(settings as SingleMaterialFFFSettings);
}
public void WriteToSettings(SingleMaterialFFFSettings settings)
{
settings.LayerHeightMM = LayerHeightMM;
settings.Shells = OuterShells;
settings.RoofLayers = RoofLayers;
settings.FloorLayers = FloorLayers;
settings.SparseLinearInfillStepX = InfillStepX;
settings.ClipSelfOverlaps = ClipSelfOverlaps;
settings.InteriorSolidRegionShells = InteriorSolidRegionShells;
settings.StartLayers = StartLayers;
settings.StartLayerHeightMM = StartLayerHeightMM;
settings.GenerateSupport = GenerateSupport;
settings.SupportOverhangAngleDeg = OverhangAngleDeg;
settings.SupportMinZTips = SupportMinZTips;
settings.EnableSupportShell = EnableSupportShell;
settings.EnableSupportReleaseOpt = EnableSupportReleaseOpt;
settings.SupportSpacingStepX = SupportStepX;
settings.SupportSolidSpace = SupportSolidSpace;
settings.EnableBridging = EnableBridging;
settings.MaxBridgeWidthMM = MaxBridgeDistanceMM;
int use_min = Math.Max(0, LayerRangeMin - 1);
int use_max = (LayerRangeMax == 0) ? 999999999 : LayerRangeMax - 1;
if (use_max < use_min)
use_max = use_min;
settings.LayerRangeFilter = new g3.Interval1i(use_min, use_max);
settings.FanSpeedX = (double)FanSpeedX / 100.0;
settings.Machine.NozzleDiamMM = NozzleDiameterMM;
settings.Machine.FilamentDiamMM = FilamentDiameterMM;
settings.ExtruderTempC = ExtruderTempC;
settings.HeatedBedTempC = BedTempC;
settings.RapidExtrudeSpeed = PrintSpeedMMS * 60;
settings.RapidTravelSpeed = TravelSpeedMMS * 60;
settings.Machine.BedSizeXMM = BedSizeXMM;
settings.Machine.BedSizeYMM = BedSizeYMM;
settings.Machine.MaxHeightMM = BedSizeZMM;
}
public SingleMaterialFFFSettings CloneCurrentSettings()
{
SingleMaterialFFFSettings clone = Active.CloneAs<SingleMaterialFFFSettings>();
WriteToSettings(clone);
return clone;
}
}
}

View file

@ -0,0 +1,403 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
namespace gs
{
/// <summary>
/// Store/restore additive settings data structures using JSON serialization
/// </summary>
public class SettingsSerializer
{
public SettingsSerializer()
{
}
/// <summary>
/// Read all .txt files in Path and try to parse each one as a Settings object
/// </summary>
public bool RestoreFromFolder(string sPath, out List<PlanarAdditiveSettings> SettingsList, out List<string> SourceFilePaths)
{
SettingsList = new List<PlanarAdditiveSettings>();
SourceFilePaths = new List<string>();
string[] files = Directory.GetFiles(sPath);
foreach (string filePath in files)
{
if (!filePath.EndsWith(".txt"))
continue;
var save_culture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
string data = File.ReadAllText(filePath);
Thread.CurrentThread.CurrentCulture = save_culture;
SingleMaterialFFFSettings settings = JsonToSettings(data);
if (settings == null)
{
//DebugUtil.Log("SettingsSerializer.RestoreFromFolder: error reading " + filePath);
}
else
{
SettingsList.Add(settings);
SourceFilePaths.Add(filePath);
}
}
return SettingsList.Count > 0;
}
static Assembly GSGCODE_ASM = null;
/// <summary>
/// Parse the json in data string into the right type of Settings object
/// </summary>
public SingleMaterialFFFSettings JsonToSettings(string data)
{
if (!is_settings_json(data))
return null;
// [RMS] because we split gsGCode into a separate dll, Type.GetType() will not find it.
// We have to use a handle to the relevant assembly.
if (GSGCODE_ASM == null)
GSGCODE_ASM = Assembly.Load("gsGCode");
int typeIdx = data.IndexOf("\"ClassTypeName\"");
SingleMaterialFFFSettings settings = null;
if (typeIdx > 0)
{
try
{
int commaIdx = typeIdx + 1;
while (data[commaIdx] != ',')
commaIdx++;
int startIdx = typeIdx + 18;
string className = data.Substring(startIdx, commaIdx - startIdx - 1);
//var type = Type.GetType(className);
var type = GSGCODE_ASM.GetType(className);
object o = JsonConvert.DeserializeObject(data, type);
settings = o as SingleMaterialFFFSettings;
}
catch
{
// ignore disasters
}
}
// either no typename, or we failed to construct it
if (settings == null)
{
try
{
settings = JsonConvert.DeserializeObject<SingleMaterialFFFSettings>(data);
}
catch
{
}
}
return settings;
}
bool is_settings_json(string s)
{
return s.Contains("ManufacturerUUID") && s.Contains("ModelUUID");
}
//public void RefreshSettingsFromDisk(MachineDatabase db, MachinePreset preset)
//{
// if (File.Exists(preset.SourcePath) == false)
// throw new Exception("SettingsSerializer.RefreshSettingsFromDisk: path " + preset.SourcePath + " does not exist!");
// var save_culture = Thread.CurrentThread.CurrentCulture;
// Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
// string data = File.ReadAllText(preset.SourcePath);
// Thread.CurrentThread.CurrentCulture = save_culture;
// SingleMaterialFFFSettings settings = JsonToSettings(data);
// if ( settings == null )
// throw new Exception("SettingsSerializer.RefreshSettingsFromDisk: json data could not be parsed!");
// preset.Settings = settings;
//}
//public void StoreSettings(MachineDatabase db, MachinePreset preset, bool bCreate = false)
//{
// JsonSerializerSettings jsonSettings = makeWriteSerializer();
// if (bCreate == false && File.Exists(preset.SourcePath) == false)
// throw new Exception("SettingsSerializer.StoreSettings: path " + preset.SourcePath + " does not exist!");
// var save_culture = Thread.CurrentThread.CurrentCulture;
// Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
// string json = JsonConvert.SerializeObject(preset.Settings, jsonSettings);
// System.IO.File.WriteAllText(preset.SourcePath, json);
// Thread.CurrentThread.CurrentCulture = save_culture;
//}
//public void UpdateSettingsFromJson(MachineDatabase db, MachinePreset preset, string newJson, bool bCreate)
//{
// JsonSerializerSettings jsonSettings = makeWriteSerializer();
// if (bCreate == false && File.Exists(preset.SourcePath) == false)
// throw new Exception("SettingsSerializer.StoreSettings: path " + preset.SourcePath + " does not exist!");
// var save_culture = Thread.CurrentThread.CurrentCulture;
// Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
// System.IO.File.WriteAllText(preset.SourcePath, newJson);
// Thread.CurrentThread.CurrentCulture = save_culture;
// RefreshSettingsFromDisk(db, preset);
//}
public bool ValidateSettingsJson(string json)
{
// [RMS] this just checks if string is valid json...
if (!is_valid_json(json))
return false;
var settings = JsonToSettings(json);
if (settings == null)
return false;
return true;
}
/// <summary>
/// check if input string is valid json
/// details: https://stackoverflow.com/questions/14977848/how-to-make-sure-that-string-is-valid-json-using-json-net
/// </summary>
protected bool is_valid_json(string json)
{
string strInput = json.Trim();
if ((strInput.StartsWith("{") && strInput.EndsWith("}")) || //For object
(strInput.StartsWith("[") && strInput.EndsWith("]"))) //For array
{
try
{
var obj = JToken.Parse(strInput);
return true;
}
catch (JsonReaderException jex)
{
//Exception in parsing json
Console.WriteLine(jex.Message);
return false;
}
catch (Exception ex) //some other exception
{
Console.WriteLine(ex.ToString());
return false;
}
}
else
{
return false;
}
}
//public void GenerateSettingsFolder(MachineDatabase db, string rootPath)
//{
// JsonSerializerSettings jsonSettings = makeWriteSerializer();
// foreach ( Manufacturer mfg in db.Manufacturers ) {
// string mfgPath = Path.Combine(rootPath, mfg.Name);
// if (!Directory.Exists(mfgPath)) {
// Directory.CreateDirectory(mfgPath);
// if (!Directory.Exists(mfgPath))
// throw new Exception("SettingsSerializer: cannot create directory for manufacturer " + mfg.Name);
// }
// IReadOnlyList<MachineModel> machines = db.ModelsForManufacturer(mfg);
// foreach ( MachineModel machine in machines) {
// string machinePath = Path.Combine(mfgPath, machine.Name);
// if (! Directory.Exists(machinePath) ) {
// Directory.CreateDirectory(machinePath);
// if (!Directory.Exists(machinePath))
// throw new Exception("SettingsSerializer: cannot create directory for machine " + mfg.Name + "::" + machine.Name);
// }
// PlanarAdditiveSettings settings = machine.DefaultPreset.Settings;
// string settingsPath = Path.Combine(machinePath, settings.Identifier + ".txt");
// var save_culture = Thread.CurrentThread.CurrentCulture;
// Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
// string json = JsonConvert.SerializeObject(settings, jsonSettings);
// System.IO.File.WriteAllText(settingsPath, json);
// Thread.CurrentThread.CurrentCulture = save_culture;
// }
// }
//}
//public string CreateNewSettingsFolder(MachineDatabase db, Manufacturer mfg, MachineModel machine, string rootPath)
//{
// string mfgPath = Path.Combine(rootPath, mfg.Name);
// if (!Directory.Exists(mfgPath)) {
// Directory.CreateDirectory(mfgPath);
// if (!Directory.Exists(mfgPath))
// throw new Exception("SettingsSerializer: cannot create directory for manufacturer " + mfg.Name);
// }
// string machinePath = Path.Combine(mfgPath, machine.Name);
// if (!Directory.Exists(machinePath)) {
// Directory.CreateDirectory(machinePath);
// if (!Directory.Exists(machinePath))
// throw new Exception("SettingsSerializer: cannot create directory for machine " + mfg.Name + "::" + machine.Name);
// }
// return machinePath;
//}
JsonSerializerSettings makeWriteSerializer()
{
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(
new Newtonsoft.Json.Converters.StringEnumConverter());
jsonSettings.Formatting = Formatting.Indented;
jsonSettings.NullValueHandling = NullValueHandling.Ignore;
jsonSettings.ContractResolver = SettingsContractResolver.MyInstance;
return jsonSettings;
}
JsonSerializerSettings makeReadSerializer()
{
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(
new Newtonsoft.Json.Converters.StringEnumConverter());
jsonSettings.Formatting = Formatting.Indented;
jsonSettings.NullValueHandling = NullValueHandling.Ignore;
return jsonSettings;
}
}
/// <summary>
/// JSON.Net settings contract - this lets you sort and filter the fields that get serialized
/// </summary>
public class SettingsContractResolver : DefaultContractResolver
{
public static readonly SettingsContractResolver MyInstance = new SettingsContractResolver();
public HashSet<string> IgnoreFields = new HashSet<string>();
public SettingsContractResolver()
{
IgnoreFields.Add("LayerRangeFilter");
IgnoreFields.Add("BaseMachine");
}
// use this to filter out fields
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(SingleMaterialFFFSettings) && IgnoreFields.Contains(property.PropertyName))
{
property.Ignored = true;
}
return property;
}
// sort property names alphabetically
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
List<JsonProperty> sorted =
base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
if (type == typeof(FFFMachineInfo))
sorted = SortMachine(sorted);
// [TODO] construct an ordering here?
return sorted;
}
static readonly string[] machine_order = new string[] {
"ManufacturerName",
"ManufacturerUUID",
"ModelIdentifier",
"ModelUUID",
"Class",
"BedSizeXMM",
"BedSizeYMM",
"MaxHeightMM",
"FilamentDiamMM",
"NozzleDiamMM",
"MinExtruderTempC",
"MaxExtruderTempC",
"HasHeatedBed",
"MinBedTempC",
"MaxBedTempC",
"MinLayerHeightMM",
"MaxLayerHeightMM"
};
List<JsonProperty> SortMachine(List<JsonProperty> input)
{
List<JsonProperty> sorted = new List<JsonProperty>(input.Count);
bool[] used = new bool[input.Count];
for (int i = 0; i < machine_order.Length; ++i)
{
int idx = input.FindIndex((v) => { return v.PropertyName.Equals(machine_order[i]); });
if (idx >= 0)
{
sorted.Add(input[idx]);
}
used[idx] = true;
}
for (int i = 0; i < input.Count; ++i)
{
if (used[i] == false)
sorted.Add(input[i]);
}
return sorted;
}
}
}

View file

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using g3;
using gs;
namespace cotangent
{
public class ToolpathGenerator
{
private object active_compute_lock = new object();
public GCodeFile CurrentGCode;
public ToolpathSet Toolpaths;
public LayersDetector LayerInfo;
public SingleMaterialFFFSettings Settings;
public PlanarSliceStack Slices;
public bool ToolpathsValid;
public bool ToolpathsFailed;
public bool ShowActualGCodePaths = false;
public bool PauseToolpathing = false;
public ToolpathGenerator()
{
}
public void CreateToolPaths(PrintMeshAssembly meshes, PlanarSliceStack slices, PrintSettings settings)
{
// have to wait for valid slice stack
if (slices == null)
{
return;
}
//mark_spawn_time();
this.BuildToolPaths(new GenerationTask
{
PrintSettings = settings.CloneCurrentSettings(),
Meshes = meshes,
SliceSet = slices,
InterpretGCodePaths = ShowActualGCodePaths
}, settings);
}
private void BuildToolPaths(GenerationTask generationTask, PrintSettings settings)
{
lock (active_compute_lock)
{
DebugUtil.Log("[ToolpathGenerator] Spawning Compute!!");
generationTask.Compute(settings);
if (generationTask.Success)
{
CurrentGCode = generationTask.gcode;
Toolpaths = generationTask.paths;
LayerInfo = generationTask.layerInfo;
Settings = generationTask.PrintSettings;
Slices = generationTask.SliceSet;
ToolpathsValid = true;
ToolpathsFailed = false;
//CC.Objects.SetToolpaths(this);
}
else
{
CurrentGCode = null;
Toolpaths = null;
LayerInfo = null;
Settings = null;
Slices = null;
ToolpathsValid = false;
ToolpathsFailed = true;
}
}
}
class GenerationTask
{
// input data
public SingleMaterialFFFSettings PrintSettings;
public PrintMeshAssembly Meshes;
public PlanarSliceStack SliceSet;
public bool InterpretGCodePaths;
// computed data
public bool Finished;
public bool Success;
public bool RequestCancel;
public GCodeFile gcode;
public ToolpathSet paths;
public LayersDetector layerInfo;
// internal
SingleMaterialFFFPrintGenerator printer;
public GenerationTask()
{
Finished = false;
Success = false;
RequestCancel = false;
}
public void Compute(PrintSettings settings)
{
RequestCancel = false;
printer =
new SingleMaterialFFFPrintGenerator(Meshes, SliceSet, PrintSettings);
if (PrintSettings.EnableSupportReleaseOpt)
{
printer.LayerPostProcessor = new SupportConnectionPostProcessor()
{
ZOffsetMM = PrintSettings.SupportReleaseGap
};
}
// if we aren't interpreting GCode, we want generator to return its path set
printer.AccumulatePathSet = (InterpretGCodePaths == false);
// set clip region
Box2d clip_box = new Box2d(Vector2d.Zero,
new Vector2d(settings.BedSizeXMM / 2, settings.BedSizeYMM / 2));
printer.PathClipRegions = new List<GeneralPolygon2d>() {
new GeneralPolygon2d(new Polygon2d(clip_box.ComputeVertices()))
};
printer.ErrorF = (msg, trace) =>
{
if (RequestCancel == false)
DebugUtil.Log(2, "Slicer Error! msg: {0} stack {1}", msg, trace);
};
DebugUtil.Log(2, "Generating gcode...");
try
{
if (printer.Generate() == false)
throw new Exception("generate failed"); // this will be caught below
gcode = printer.Result;
//DebugUtil.Log(2, "Interpreting gcode...");
if (InterpretGCodePaths)
{
GCodeToToolpaths converter = new GCodeToToolpaths();
MakerbotInterpreter interpreter = new MakerbotInterpreter();
interpreter.AddListener(converter);
InterpretArgs interpArgs = new InterpretArgs();
interpreter.Interpret(gcode, interpArgs);
paths = converter.PathSet;
}
else
paths = printer.AccumulatedPaths;
//DebugUtil.Log(2, "Detecting layers...");
layerInfo = new LayersDetector(paths);
Success = true;
}
catch (Exception e)
{
DebugUtil.Log("ToolpathGenerator.Compute: exception: " + e.Message);
Success = false;
}
Finished = true;
}
}
}
}

View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2019, 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 MatterHackers.MatterControl.Extensibility;
using MatterHackers.MatterControl.SlicerConfiguration;
namespace MatterHackers.gsBundle
{
public class gsSlicerPlugin : IApplicationPlugin
{
public void Initialize()
{
PrinterSettings.Slicer = new BasicSlicer();
}
public PluginInfo MetaData => new PluginInfo()
{
About = "gsSlicer for MatterControl",
Developer = "MatterHackers Inc.",
Name = "MH-gsSlicer",
Url = "https://www.matterhackers.com/MatterControl",
UUID = "0384EBD9-072F-4295-AE48-270FC256B48B"
};
}
}

@ -1 +1 @@
Subproject commit 29ad0540f5742e1a196174cdaf430410921b2cfc
Subproject commit 98150c5a700d51594df7de70e576a21a12912a94

@ -0,0 +1 @@
Subproject commit 595abad18cb7818eea14bad060b3e958118af44e

1
Submodules/gsGCode Submodule

@ -0,0 +1 @@
Subproject commit 0d9c4761e7e0ad35c7feb5094635c3b81aedff82

1
Submodules/gsSlicer Submodule

@ -0,0 +1 @@
Subproject commit 4ca5200c3346a3df572deb2b189f22060a8348f6