From 7a27433bf08725a8cb1ce3868d9ad39164f8844a Mon Sep 17 00:00:00 2001 From: John Lewin Date: Wed, 21 Jun 2017 23:26:20 -0700 Subject: [PATCH 01/18] Migrate GCodeVisualizer and MeshViewer content into MatterControl --- .../GCodeRenderer/ColorVertexData.cs | 71 ++ .../GCodeRenderer/ExtrusionColors.cs | 76 ++ .../GCodeRenderer/GCodeRenderInfo.cs | 103 ++ .../GCodeRenderer/GCodeRenderer.cs | 406 ++++++++ .../GCodeRenderer/GCodeVertexBuffer.cs | 136 +++ .../RenderFeatures/RenderFeatureBase.cs | 183 ++++ .../RenderFeatures/RenderFeatureExtrusion.cs | 157 +++ .../RenderFeatures/RenderFeatureRetract.cs | 138 +++ .../RenderFeatures/RenderFeatureTravel.cs | 138 +++ .../MatterControl.OpenGL.csproj | 80 ++ .../Properties/AssemblyInfo.cs | 36 + MatterControl.Printing/GCode/GCodeFile.cs | 254 +++++ .../GCode/GCodeFileLoaded.cs | 969 ++++++++++++++++++ .../GCode/GCodeFileStreamed.cs | 286 ++++++ .../GCode/PrinterMachineInstruction.cs | 152 +++ .../MatterControl.Printing.csproj | 61 ++ .../Properties/AssemblyInfo.cs | 36 + MatterControl.csproj | 19 +- PartPreviewWindow/View3D/InteractionVolume.cs | 206 ++++ PartPreviewWindow/View3D/MeshViewerWidget.cs | 888 ++++++++++++++++ PartPreviewWindow/View3D/MouseEvent3DArgs.cs | 52 + PartPreviewWindow/ViewGcodeBasic.cs | 4 +- PartPreviewWindow/ViewGcodeWidget.cs | 13 +- .../MatterControl.Tests.csproj | 12 +- TextCreator/TextCreator.csproj | 4 - 25 files changed, 4452 insertions(+), 28 deletions(-) create mode 100644 MatterControl.OpenGL/GCodeRenderer/ColorVertexData.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/ExtrusionColors.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/GCodeRenderInfo.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/GCodeRenderer.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/GCodeVertexBuffer.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureBase.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureExtrusion.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureRetract.cs create mode 100644 MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureTravel.cs create mode 100644 MatterControl.OpenGL/MatterControl.OpenGL.csproj create mode 100644 MatterControl.OpenGL/Properties/AssemblyInfo.cs create mode 100644 MatterControl.Printing/GCode/GCodeFile.cs create mode 100644 MatterControl.Printing/GCode/GCodeFileLoaded.cs create mode 100644 MatterControl.Printing/GCode/GCodeFileStreamed.cs create mode 100644 MatterControl.Printing/GCode/PrinterMachineInstruction.cs create mode 100644 MatterControl.Printing/MatterControl.Printing.csproj create mode 100644 MatterControl.Printing/Properties/AssemblyInfo.cs create mode 100644 PartPreviewWindow/View3D/InteractionVolume.cs create mode 100644 PartPreviewWindow/View3D/MeshViewerWidget.cs create mode 100644 PartPreviewWindow/View3D/MouseEvent3DArgs.cs diff --git a/MatterControl.OpenGL/GCodeRenderer/ColorVertexData.cs b/MatterControl.OpenGL/GCodeRenderer/ColorVertexData.cs new file mode 100644 index 000000000..83e9c7b2d --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/ColorVertexData.cs @@ -0,0 +1,71 @@ +using MatterHackers.Agg; +using MatterHackers.VectorMath; + +/* +Copyright (c) 2014, Lars Brubaker +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.Runtime.InteropServices; + +namespace MatterHackers.GCodeVisualizer +{ + [StructLayout(LayoutKind.Sequential)] + public struct ColorVertexData + { + public byte r; + public byte g; + public byte b; + public byte a; + + public float normalX; + public float normalY; + public float normalZ; + + public float positionX; + public float positionY; + public float positionZ; + + public static readonly int Stride = Marshal.SizeOf(default(ColorVertexData)); + + public ColorVertexData(Vector3 position, Vector3 normal, RGBA_Bytes color) + { + r = (byte)color.Red0To255; + g = (byte)color.Green0To255; + b = (byte)color.Blue0To255; + a = (byte)color.Alpha0To255; + + normalX = (float)normal.x; + normalY = (float)normal.y; + normalZ = (float)normal.z; + + positionX = (float)position.x; + positionY = (float)position.y; + positionZ = (float)position.z; + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/ExtrusionColors.cs b/MatterControl.OpenGL/GCodeRenderer/ExtrusionColors.cs new file mode 100644 index 000000000..ba360e933 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/ExtrusionColors.cs @@ -0,0 +1,76 @@ +using MatterHackers.Agg; + +/* +Copyright (c) 2014, Lars Brubaker +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.Collections.Generic; +using System.Linq; + +namespace MatterHackers.GCodeVisualizer +{ + public class ExtrusionColors + { + private SortedList speedColorLookup = new SortedList(); + + public RGBA_Bytes GetColorForSpeed(float speed) + { + if (speed > 0) + { + lock(speedColorLookup) + { + double startColor = 223.0 / 360.0; + double endColor = 5.0 / 360.0; + double delta = startColor - endColor; + + if (!speedColorLookup.ContainsKey(speed)) + { + RGBA_Bytes color = RGBA_Floats.FromHSL(startColor, .99, .49).GetAsRGBA_Bytes(); + speedColorLookup.Add(speed, color); + + if (speedColorLookup.Count > 1) + { + double step = delta / (speedColorLookup.Count - 1); + for (int index = 0; index < speedColorLookup.Count; index++) + { + double offset = step * index; + double fixedColor = startColor - offset; + KeyValuePair keyValue = speedColorLookup.ElementAt(index); + speedColorLookup[keyValue.Key] = RGBA_Floats.FromHSL(fixedColor, .99, .49).GetAsRGBA_Bytes(); + } + } + } + + return speedColorLookup[speed]; + } + } + + return RGBA_Bytes.Black; + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/GCodeRenderInfo.cs b/MatterControl.OpenGL/GCodeRenderer/GCodeRenderInfo.cs new file mode 100644 index 000000000..387ba9271 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/GCodeRenderInfo.cs @@ -0,0 +1,103 @@ +/* +Copyright (c) 2014, Lars Brubaker +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 MatterHackers.Agg; +using MatterHackers.Agg.Transform; +using MatterHackers.VectorMath; + +namespace MatterHackers.GCodeVisualizer +{ + public class GCodeRenderInfo + { + private Vector2[] extruderOffsets; + + public Vector2 GetExtruderOffset(int index) + { + if (extruderOffsets != null + && extruderOffsets.Length > index) + { + return extruderOffsets[index]; + } + + return Vector2.Zero; + } + + public Func GetMaterialColor { get; } + + public int startLayerIndex; + + public int StartLayerIndex { get { return startLayerIndex; } } + + private int endLayerIndex; + + public int EndLayerIndex { get { return endLayerIndex; } } + + private Affine transform; + + public Affine Transform { get { return transform; } } + + private double layerScale; + + public double LayerScale { get { return layerScale; } } + + private RenderType currentRenderType; + + public RenderType CurrentRenderType { get { return currentRenderType; } } + + private double featureToStartOnRatio0To1; + + public double FeatureToStartOnRatio0To1 { get { return featureToStartOnRatio0To1; } } + + private double featureToEndOnRatio0To1; + + public double FeatureToEndOnRatio0To1 { get { return featureToEndOnRatio0To1; } } + + public GCodeRenderInfo() + { + } + + public GCodeRenderInfo(int startLayerIndex, int endLayerIndex, + Affine transform, double layerScale, RenderType renderType, + double featureToStartOnRatio0To1, double featureToEndOnRatio0To1, + Vector2[] extruderOffsets, + Func getMaterialColor) + { + this.GetMaterialColor = getMaterialColor; + this.startLayerIndex = startLayerIndex; + this.endLayerIndex = endLayerIndex; + this.transform = transform; + this.layerScale = layerScale; + this.currentRenderType = renderType; + this.featureToStartOnRatio0To1 = featureToStartOnRatio0To1; + this.featureToEndOnRatio0To1 = featureToEndOnRatio0To1; + this.extruderOffsets = extruderOffsets; + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/GCodeRenderer.cs b/MatterControl.OpenGL/GCodeRenderer/GCodeRenderer.cs new file mode 100644 index 000000000..8bafd1714 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/GCodeRenderer.cs @@ -0,0 +1,406 @@ +/* +Copyright (c) 2017, Lars Brubaker, 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 MatterHackers.Agg; +using MatterHackers.RenderOpenGl; +using MatterHackers.RenderOpenGl.OpenGl; + +namespace MatterHackers.GCodeVisualizer +{ + [Flags] + public enum RenderType + { + None = 0, + Extrusions = 1, + Moves = 2, + Retractions = 4, + SpeedColors = 8, + SimulateExtrusion = 16, + HideExtruderOffsets = 32, + TransparentExtrusion = 64, + }; + + public class GCodeRenderer : IDisposable + { + public static double ExtruderWidth { get; set; } = .4; + + private List> featureStartIndex = new List>(); + private List> featureEndIndex = new List>(); + private List> renderFeatures = new List>(); + + public static RGBA_Bytes ExtrusionColor = RGBA_Bytes.White; + public static RGBA_Bytes TravelColor = RGBA_Bytes.Green; + + private GCodeFile gCodeFileToDraw; + + public GCodeFile GCodeFileToDraw { get { return gCodeFileToDraw; } } + + private ExtrusionColors extrusionColors; + + public GCodeRenderer(GCodeFile gCodeFileToDraw) + { + if (gCodeFileToDraw != null) + { + this.gCodeFileToDraw = gCodeFileToDraw; + + for (int i = 0; i < gCodeFileToDraw.NumChangesInZ; i++) + { + renderFeatures.Add(new List()); + } + } + } + + public void CreateFeaturesForLayerIfRequired(int layerToCreate) + { + if (extrusionColors == null + && gCodeFileToDraw != null + && gCodeFileToDraw.LineCount > 0) + { + extrusionColors = new ExtrusionColors(); + HashSet speeds = new HashSet(); + PrinterMachineInstruction prevInstruction = gCodeFileToDraw.Instruction(0); + for (int i = 1; i < gCodeFileToDraw.LineCount; i++) + { + PrinterMachineInstruction instruction = gCodeFileToDraw.Instruction(i); + if (instruction.EPosition > prevInstruction.EPosition && (instruction.Line.IndexOf('X') != -1 || instruction.Line.IndexOf('Y') != -1)) + { + speeds.Add((float)instruction.FeedRate); + } + + prevInstruction = instruction; + } + + foreach (float speed in speeds) + { + extrusionColors.GetColorForSpeed(speed); + } + } + + if (renderFeatures.Count == 0 + || renderFeatures[layerToCreate].Count > 0) + { + return; + } + + List renderFeaturesForLayer = renderFeatures[layerToCreate]; + + int startRenderIndex = gCodeFileToDraw.GetInstructionIndexAtLayer(layerToCreate); + int endRenderIndex = gCodeFileToDraw.LineCount - 1; + if (layerToCreate < gCodeFileToDraw.NumChangesInZ - 1) + { + endRenderIndex = gCodeFileToDraw.GetInstructionIndexAtLayer(layerToCreate + 1); + } + + for (int instructionIndex = startRenderIndex; instructionIndex < endRenderIndex; instructionIndex++) + { + PrinterMachineInstruction currentInstruction = gCodeFileToDraw.Instruction(instructionIndex); + PrinterMachineInstruction previousInstruction = currentInstruction; + if (instructionIndex > 0) + { + previousInstruction = gCodeFileToDraw.Instruction(instructionIndex - 1); + } + + if (currentInstruction.Position == previousInstruction.Position) + { + if (Math.Abs(currentInstruction.EPosition - previousInstruction.EPosition) > 0) + { + // this is a retraction + renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, currentInstruction.EPosition - previousInstruction.EPosition, currentInstruction.ExtruderIndex, currentInstruction.FeedRate)); + } + if (currentInstruction.Line.StartsWith("G10")) + { + renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, -1, currentInstruction.ExtruderIndex, currentInstruction.FeedRate)); + } + else if (currentInstruction.Line.StartsWith("G11")) + { + renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, 1, currentInstruction.ExtruderIndex, currentInstruction.FeedRate)); + } + } + else + { + if (gCodeFileToDraw.IsExtruding(instructionIndex)) + { + double layerThickness = gCodeFileToDraw.GetLayerHeight(); + if (layerToCreate == 0) + { + layerThickness = gCodeFileToDraw.GetFirstLayerHeight(); + } + + RGBA_Bytes extrusionColor = extrusionColors.GetColorForSpeed((float)currentInstruction.FeedRate); + renderFeaturesForLayer.Add(new RenderFeatureExtrusion(previousInstruction.Position, currentInstruction.Position, currentInstruction.ExtruderIndex, currentInstruction.FeedRate, currentInstruction.EPosition - previousInstruction.EPosition, gCodeFileToDraw.GetFilamentDiameter(), layerThickness, extrusionColor)); + } + else + { + renderFeaturesForLayer.Add(new RenderFeatureTravel(previousInstruction.Position, currentInstruction.Position, currentInstruction.ExtruderIndex, currentInstruction.FeedRate)); + } + } + } + } + + public int GetNumFeatures(int layerToCountFeaturesOn) + { + CreateFeaturesForLayerIfRequired(layerToCountFeaturesOn); + return renderFeatures[layerToCountFeaturesOn].Count; + } + + public void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo) + { + if (renderFeatures.Count > 0) + { + CreateFeaturesForLayerIfRequired(renderInfo.EndLayerIndex); + + int featuresOnLayer = renderFeatures[renderInfo.EndLayerIndex].Count; + int endFeature = (int)(featuresOnLayer * renderInfo.FeatureToEndOnRatio0To1 + .5); + endFeature = Math.Max(0, Math.Min(endFeature, featuresOnLayer)); + + int startFeature = (int)(featuresOnLayer * renderInfo.FeatureToStartOnRatio0To1 + .5); + startFeature = Math.Max(0, Math.Min(startFeature, featuresOnLayer)); + + // try to make sure we always draw at least one feature + if (endFeature <= startFeature) + { + endFeature = Math.Min(startFeature + 1, featuresOnLayer); + } + if (startFeature >= endFeature) + { + // This can only happen if the start and end are set to the last feature + // Try to set the start feature to one from the end + startFeature = Math.Max(endFeature - 1, 0); + } + + Graphics2DOpenGL graphics2DGl = graphics2D as Graphics2DOpenGL; + if (graphics2DGl != null) + { + graphics2DGl.PreRender(); + GL.Begin(BeginMode.Triangles); + for (int i = startFeature; i < endFeature; i++) + { + RenderFeatureBase feature = renderFeatures[renderInfo.EndLayerIndex][i]; + if (feature != null) + { + feature.Render(graphics2DGl, renderInfo); + } + } + GL.End(); + graphics2DGl.PopOrthoProjection(); + } + else + { + for (int i = startFeature; i < endFeature; i++) + { + RenderFeatureBase feature = renderFeatures[renderInfo.EndLayerIndex][i]; + if (feature != null) + { + feature.Render(graphics2D, renderInfo); + } + } + } + } + } + + private void Create3DDataForLayer(int layerIndex, + VectorPOD colorVertexData, + VectorPOD vertexIndexArray, + GCodeRenderInfo renderInfo) + { + colorVertexData.Clear(); + vertexIndexArray.Clear(); + featureStartIndex[layerIndex].Clear(); + featureEndIndex[layerIndex].Clear(); + + for (int i = 0; i < renderFeatures[layerIndex].Count; i++) + { + featureStartIndex[layerIndex].Add(vertexIndexArray.Count); + RenderFeatureBase feature = renderFeatures[layerIndex][i]; + if (feature != null) + { + feature.CreateRender3DData(colorVertexData, vertexIndexArray, renderInfo); + } + featureEndIndex[layerIndex].Add(vertexIndexArray.Count); + } + } + + public void Dispose() + { + Clear3DGCode(); + } + + public void Clear3DGCode() + { + if (layerVertexBuffer != null) + { + for (int i = 0; i < layerVertexBuffer.Count; i++) + { + if (layerVertexBuffer[i] != null) + { + layerVertexBuffer[i].Dispose(); + layerVertexBuffer[i] = null; + } + } + } + } + + private List layerVertexBuffer; + private RenderType lastRenderType = RenderType.None; + + private static readonly bool Is32Bit = IntPtr.Size == 4; + + public void Render3D(GCodeRenderInfo renderInfo) + { + if (layerVertexBuffer == null) + { + layerVertexBuffer = new List(); + layerVertexBuffer.Capacity = gCodeFileToDraw.NumChangesInZ; + for (int layerIndex = 0; layerIndex < gCodeFileToDraw.NumChangesInZ; layerIndex++) + { + layerVertexBuffer.Add(null); + featureStartIndex.Add(new List()); + featureEndIndex.Add(new List()); + } + } + + for (int layerIndex = 0; layerIndex < gCodeFileToDraw.NumChangesInZ; layerIndex++) + { + CreateFeaturesForLayerIfRequired(layerIndex); + } + + if (lastRenderType != renderInfo.CurrentRenderType) + { + Clear3DGCode(); + lastRenderType = renderInfo.CurrentRenderType; + } + + if (renderFeatures.Count > 0) + { + if (Is32Bit && !GL.GlHasBufferObjects) + { + int maxFeaturesForThisSystem = 125000; + int totalFeaturesToRunder = 0; + bool cleanUnusedLayers = false; + // if on 32 bit system make sure we don't run out of memory rendering too many features + for (int i = renderInfo.EndLayerIndex - 1; i >= renderInfo.StartLayerIndex; i--) + { + if (totalFeaturesToRunder + renderFeatures[i].Count < maxFeaturesForThisSystem) + { + totalFeaturesToRunder += renderFeatures[i].Count; + } + else // don't render any of the layers below this and in fact remove them from memory if possible + { + renderInfo.startLayerIndex = i + 1; + cleanUnusedLayers = true; + break; + } + } + + if (cleanUnusedLayers) + { + // no remove any layers that are set that we are not going to render + for (int removeIndex = 0; removeIndex < layerVertexBuffer.Count; removeIndex++) + { + if (removeIndex < renderInfo.StartLayerIndex || removeIndex >= renderInfo.EndLayerIndex) + { + if (layerVertexBuffer[removeIndex] != null) + { + layerVertexBuffer[removeIndex].Dispose(); + layerVertexBuffer[removeIndex] = null; + } + } + } + } + } + + for (int i = renderInfo.EndLayerIndex - 1; i >= renderInfo.StartLayerIndex; i--) + { + // If its the first render or we change what we are trying to render then create vertex data. + if (layerVertexBuffer[i] == null) + { + VectorPOD colorVertexData = new VectorPOD(); + VectorPOD vertexIndexArray = new VectorPOD(); + + Create3DDataForLayer(i, colorVertexData, vertexIndexArray, renderInfo); + + layerVertexBuffer[i] = new GCodeVertexBuffer(); + layerVertexBuffer[i].SetVertexData(colorVertexData.Array); + layerVertexBuffer[i].SetIndexData(vertexIndexArray.Array); + } + } + + GL.Disable(EnableCap.Texture2D); + GL.PushAttrib(AttribMask.EnableBit); + GL.DisableClientState(ArrayCap.TextureCoordArray); + GL.Enable(EnableCap.PolygonSmooth); + + if (renderInfo.EndLayerIndex - 1 > renderInfo.StartLayerIndex) + { + for (int i = renderInfo.StartLayerIndex; i < renderInfo.EndLayerIndex - 1; i++) + { + int featuresOnLayer = renderFeatures[i].Count; + if (featuresOnLayer > 1) + { + layerVertexBuffer[i].renderRange(0, featureEndIndex[i][featuresOnLayer - 1]); + } + } + } + + // draw the partial layer of end-1 from startRatio to endRatio + { + int layerIndex = renderInfo.EndLayerIndex - 1; + int featuresOnLayer = renderFeatures[layerIndex].Count; + int startFeature = (int)(featuresOnLayer * renderInfo.FeatureToStartOnRatio0To1 + .5); + startFeature = Math.Max(0, Math.Min(startFeature, featuresOnLayer)); + + int endFeature = (int)(featuresOnLayer * renderInfo.FeatureToEndOnRatio0To1 + .5); + endFeature = Math.Max(0, Math.Min(endFeature, featuresOnLayer)); + + // try to make sure we always draw at least one feature + if (endFeature <= startFeature) + { + endFeature = Math.Min(startFeature + 1, featuresOnLayer); + } + if (startFeature >= endFeature) + { + // This can only happen if the start and end are set to the last feature + // Try to set the start feature to one from the end + startFeature = Math.Max(endFeature - 1, 0); + } + + if (endFeature > startFeature) + { + int ellementCount = featureEndIndex[layerIndex][endFeature - 1] - featureStartIndex[layerIndex][startFeature]; + + layerVertexBuffer[layerIndex].renderRange(featureStartIndex[layerIndex][startFeature], ellementCount); + } + } + GL.PopAttrib(); + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/GCodeVertexBuffer.cs b/MatterControl.OpenGL/GCodeRenderer/GCodeVertexBuffer.cs new file mode 100644 index 000000000..eb7692435 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/GCodeVertexBuffer.cs @@ -0,0 +1,136 @@ +using MatterHackers.Agg.UI; +using MatterHackers.RenderOpenGl.OpenGl; + +/* +Copyright (c) 2014, Lars Brubaker +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; + +namespace MatterHackers.GCodeVisualizer +{ + public class GCodeVertexBuffer : IDisposable + { + public int myIndexId; + public int myIndexLength; + public BeginMode myMode = BeginMode.Triangles; + public int myVertexId; + public int myVertexLength; + public GCodeVertexBuffer() + { + GL.GenBuffers(1, out myVertexId); + GL.GenBuffers(1, out myIndexId); + } + + public void Dispose() + { + if (myVertexId != -1) + { + int holdVertexId = myVertexId; + int holdIndexId = myIndexId; + UiThread.RunOnIdle(() => + { + GL.DeleteBuffers(1, ref holdVertexId); + GL.DeleteBuffers(1, ref holdIndexId); + }); + + myVertexId = -1; + } + } + + ~GCodeVertexBuffer() + { + Dispose(); + } + + public void renderRange(int offset, int count) + { + GL.EnableClientState(ArrayCap.ColorArray); + GL.EnableClientState(ArrayCap.NormalArray); + GL.EnableClientState(ArrayCap.VertexArray); + GL.DisableClientState(ArrayCap.TextureCoordArray); + GL.Disable(EnableCap.Texture2D); + + GL.EnableClientState(ArrayCap.IndexArray); + + GL.BindBuffer(BufferTarget.ArrayBuffer, myVertexId); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, myIndexId); + + GL.ColorPointer(4, ColorPointerType.UnsignedByte, ColorVertexData.Stride, new IntPtr(0)); + GL.NormalPointer(NormalPointerType.Float, ColorVertexData.Stride, new IntPtr(4)); + GL.VertexPointer(3, VertexPointerType.Float, ColorVertexData.Stride, new IntPtr(4 + 3 * 4)); + + GL.DrawRangeElements(myMode, 0, myIndexLength, count, DrawElementsType.UnsignedInt, new IntPtr(offset * 4)); + + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); + + GL.DisableClientState(ArrayCap.IndexArray); + + GL.DisableClientState(ArrayCap.VertexArray); + GL.DisableClientState(ArrayCap.NormalArray); + GL.DisableClientState(ArrayCap.ColorArray); + } + + public void SetIndexData(int[] data) + { + SetIndexData(data, data.Length); + } + + public void SetIndexData(int[] data, int count) + { + myIndexLength = count; + GL.BindBuffer(BufferTarget.ElementArrayBuffer, myIndexId); + unsafe + { + fixed (int* dataPointer = data) + { + GL.BufferData(BufferTarget.ElementArrayBuffer, data.Length * sizeof(int), (IntPtr)dataPointer, BufferUsageHint.StaticDraw); + } + } + } + + public void SetVertexData(ColorVertexData[] data) + { + SetVertexData(data, data.Length); + } + + public void SetVertexData(ColorVertexData[] data, int count) + { + myVertexLength = count; + GL.BindBuffer(BufferTarget.ArrayBuffer, myVertexId); + unsafe + { + fixed (ColorVertexData* dataPointer = data) + { + GL.BufferData(BufferTarget.ArrayBuffer, data.Length * ColorVertexData.Stride, (IntPtr)dataPointer, BufferUsageHint.StaticDraw); + } + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureBase.cs b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureBase.cs new file mode 100644 index 000000000..fa173e9b4 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureBase.cs @@ -0,0 +1,183 @@ +/* +Copyright (c) 2014, Lars Brubaker +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.Agg; +using MatterHackers.VectorMath; + +namespace MatterHackers.GCodeVisualizer +{ + public abstract class RenderFeatureBase + { + protected int extruderIndex; + + public abstract void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo); + + public abstract void CreateRender3DData(VectorPOD colorVertexData, VectorPOD indexData, GCodeRenderInfo renderInfo); + + public RenderFeatureBase(int extruderIndex) + { + this.extruderIndex = extruderIndex; + } + + static public void CreateCylinder(VectorPOD colorVertexData, VectorPOD indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, RGBA_Bytes color, double layerHeight) + { + Vector3 direction = endPos - startPos; + Vector3 directionNormal = direction.GetNormal(); + Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); + + int[] tubeStartIndices = new int[steps]; + int[] tubeEndIndices = new int[steps]; + + int[] capStartIndices = new int[steps]; + int[] capEndIndices = new int[steps]; + + double halfHeight = layerHeight / 2 + (layerHeight * .1); + double halfWidth = radius; + double zScale = halfHeight / radius; + double xScale = halfWidth / radius; + + Vector3 scale = new Vector3(xScale, xScale, zScale); + + for (int i = 0; i < steps; i++) + { + // create tube ends verts + Vector3 tubeNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + Vector3 offset = Vector3.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + offset *= scale; + + Vector3 tubeStart = startPos + offset; + tubeStartIndices[i] = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); + + Vector3 tubeEnd = endPos + offset; + tubeEndIndices[i] = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color)); + + // create cap verts + Vector3 rotateAngle = Vector3.Cross(direction, startSweepDirection); + Vector3 capStartNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8)); + capStartNormal = Vector3.Transform(capStartNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + capStartNormal = (capStartNormal * scale).GetNormal(); + Vector3 capStartOffset = capStartNormal * radius; + capStartOffset *= scale; + Vector3 capStart = startPos + capStartOffset; + capStartIndices[i] = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color)); + + Vector3 capEndNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8)); + capEndNormal = Vector3.Transform(capEndNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + capEndNormal = (capEndNormal * scale).GetNormal(); + Vector3 capEndOffset = capEndNormal * radius; + capEndOffset *= scale; + Vector3 capEnd = endPos + capEndOffset; + capEndIndices[i] = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color)); + } + + int tipStartIndex = colorVertexData.Count; + Vector3 tipOffset = directionNormal * radius; + tipOffset *= scale; + colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color)); + int tipEndIndex = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color)); + + for (int i = 0; i < steps; i++) + { + // create tube polys + indexData.Add(tubeStartIndices[i]); + indexData.Add(tubeEndIndices[i]); + indexData.Add(tubeEndIndices[(i + 1) % steps]); + + indexData.Add(tubeStartIndices[i]); + indexData.Add(tubeEndIndices[(i + 1) % steps]); + indexData.Add(tubeStartIndices[(i + 1) % steps]); + + // create start cap polys + indexData.Add(tubeStartIndices[i]); + indexData.Add(capStartIndices[i]); + indexData.Add(capStartIndices[(i + 1) % steps]); + + indexData.Add(tubeStartIndices[i]); + indexData.Add(capStartIndices[(i + 1) % steps]); + indexData.Add(tubeStartIndices[(i + 1) % steps]); + + // create end cap polys + indexData.Add(tubeEndIndices[i]); + indexData.Add(capEndIndices[i]); + indexData.Add(capEndIndices[(i + 1) % steps]); + + indexData.Add(tubeEndIndices[i]); + indexData.Add(capEndIndices[(i + 1) % steps]); + indexData.Add(tubeEndIndices[(i + 1) % steps]); + + // create start tip polys + indexData.Add(tipStartIndex); + indexData.Add(capStartIndices[i]); + indexData.Add(capStartIndices[(i + 1) % steps]); + + // create end tip polys + indexData.Add(tipEndIndex); + indexData.Add(capEndIndices[i]); + indexData.Add(capEndIndices[(i + 1) % steps]); + } + } + + static public void CreatePointer(VectorPOD colorVertexData, VectorPOD indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, RGBA_Bytes color) + { + Vector3 direction = endPos - startPos; + Vector3 directionNormal = direction.GetNormal(); + Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); + + int[] tubeStartIndices = new int[steps]; + + for (int i = 0; i < steps; i++) + { + // create tube ends verts + Vector3 tubeNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + Vector3 offset = Vector3.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); + Vector3 tubeStart = startPos + offset; + tubeStartIndices[i] = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); + Vector3 tubeEnd = endPos + offset; + } + + int tipEndIndex = colorVertexData.Count; + colorVertexData.Add(new ColorVertexData(endPos, directionNormal, color)); + + for (int i = 0; i < steps; i++) + { + // create tube polys + indexData.Add(tubeStartIndices[i]); + indexData.Add(tubeStartIndices[(i + 1) % steps]); + + indexData.Add(tipEndIndex); + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureExtrusion.cs b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureExtrusion.cs new file mode 100644 index 000000000..208a10977 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureExtrusion.cs @@ -0,0 +1,157 @@ +using MatterHackers.Agg; +using MatterHackers.Agg.VertexSource; +using MatterHackers.RenderOpenGl; +using MatterHackers.VectorMath; +/* +Copyright (c) 2014, Lars Brubaker +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; + +namespace MatterHackers.GCodeVisualizer +{ + public class RenderFeatureExtrusion : RenderFeatureTravel + { + private float extrusionVolumeMm3; + private float layerHeight; + private RGBA_Bytes color; + + public RenderFeatureExtrusion(Vector3 start, Vector3 end, int extruderIndex, double travelSpeed, double totalExtrusionMm, double filamentDiameterMm, double layerHeight, RGBA_Bytes color) + : base(start, end, extruderIndex, travelSpeed) + { + this.color = color; + double filamentRadius = filamentDiameterMm / 2; + double areaSquareMm = (filamentRadius * filamentRadius) * Math.PI; + + this.extrusionVolumeMm3 = (float)(areaSquareMm * totalExtrusionMm); + this.layerHeight = (float)layerHeight; + } + + private double GetRadius(RenderType renderType) + { + return GetExtrusionWidth(renderType) / 2; + } + + private double GetExtrusionWidth(RenderType renderType) + { + double width = GCodeRenderer.ExtruderWidth; + if ((renderType & RenderType.SimulateExtrusion) == RenderType.SimulateExtrusion) + { + double moveLength = (end - start).Length; + + if (moveLength > .1) // we get truncation errors from the slice engine when the length is very small, so don't do them + { + double area = extrusionVolumeMm3 / moveLength; + width = area / layerHeight; + } + } + + return width; + } + + public override void CreateRender3DData(VectorPOD colorVertexData, VectorPOD indexData, GCodeRenderInfo renderInfo) + { + if ((renderInfo.CurrentRenderType & RenderType.Extrusions) == RenderType.Extrusions) + { + Vector3Float start = this.GetStart(renderInfo); + Vector3Float end = this.GetEnd(renderInfo); + double radius = GetRadius(renderInfo.CurrentRenderType); + if ((renderInfo.CurrentRenderType & RenderType.SpeedColors) == RenderType.SpeedColors) + { + CreateCylinder(colorVertexData, indexData, new Vector3(start), new Vector3(end), radius, 6, color, layerHeight); + } + else + { + if (extruderIndex == 0) + { + CreateCylinder(colorVertexData, indexData, new Vector3(start), new Vector3(end), radius, 6, GCodeRenderer.ExtrusionColor, layerHeight); + } + else + { + CreateCylinder(colorVertexData, indexData, new Vector3(start), new Vector3(end), radius, 6, renderInfo.GetMaterialColor(extruderIndex + 1), layerHeight); + } + } + } + } + + public override void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo) + { + if (renderInfo.CurrentRenderType.HasFlag(RenderType.Extrusions)) + { + double extrusionLineWidths = GetExtrusionWidth(renderInfo.CurrentRenderType) * 2 * renderInfo.LayerScale; + + RGBA_Bytes extrusionColor = RGBA_Bytes.Black; + if (extruderIndex > 0) + { + extrusionColor = renderInfo.GetMaterialColor(extruderIndex + 1); + } + if (renderInfo.CurrentRenderType.HasFlag(RenderType.SpeedColors)) + { + extrusionColor = color; + } + + if (renderInfo.CurrentRenderType.HasFlag(RenderType.TransparentExtrusion)) + { + extrusionColor = new RGBA_Bytes(extrusionColor, 200); + } + + // render the part using opengl + Graphics2DOpenGL graphics2DGl = graphics2D as Graphics2DOpenGL; + if (graphics2DGl != null) + { + Vector3Float startF = this.GetStart(renderInfo); + Vector3Float endF = this.GetEnd(renderInfo); + Vector2 start = new Vector2(startF.x, startF.y); + renderInfo.Transform.transform(ref start); + + Vector2 end = new Vector2(endF.x, endF.y); + renderInfo.Transform.transform(ref end); + + graphics2DGl.DrawAALineRounded(start, end, extrusionLineWidths/2, extrusionColor); + } + else + { + PathStorage pathStorage = new PathStorage(); + VertexSourceApplyTransform transformedPathStorage = new VertexSourceApplyTransform(pathStorage, renderInfo.Transform); + Stroke stroke = new Stroke(transformedPathStorage, extrusionLineWidths/2); + + stroke.line_cap(LineCap.Round); + stroke.line_join(LineJoin.Round); + + Vector3Float start = this.GetStart(renderInfo); + Vector3Float end = this.GetEnd(renderInfo); + + pathStorage.Add(start.x, start.y, ShapePath.FlagsAndCommand.CommandMoveTo); + pathStorage.Add(end.x, end.y, ShapePath.FlagsAndCommand.CommandLineTo); + + graphics2D.Render(stroke, 0, extrusionColor); + } + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureRetract.cs b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureRetract.cs new file mode 100644 index 000000000..cc0b0d9b8 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureRetract.cs @@ -0,0 +1,138 @@ +using MatterHackers.Agg; +using MatterHackers.Agg.VertexSource; +using MatterHackers.RenderOpenGl; +using MatterHackers.VectorMath; + +/* +Copyright (c) 2014, Lars Brubaker +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; + +namespace MatterHackers.GCodeVisualizer +{ + public class RenderFeatureRetract : RenderFeatureBase + { + public static double RetractionDrawRadius = 1; + + private float extrusionAmount; + private float mmPerSecond; + private Vector3Float position; + + public RenderFeatureRetract(Vector3 position, double extrusionAmount, int extruderIndex, double mmPerSecond) + : base(extruderIndex) + { + this.extrusionAmount = (float)extrusionAmount; + this.mmPerSecond = (float)mmPerSecond; + + this.position = new Vector3Float(position); + } + + private double Radius(double layerScale) + { + double radius = RetractionDrawRadius * layerScale; + double area = Math.PI * radius * radius; + area *= Math.Abs(extrusionAmount); + radius = Math.Sqrt(area / Math.PI); + return radius; + } + + public override void CreateRender3DData(VectorPOD colorVertexData, VectorPOD indexData, GCodeRenderInfo renderInfo) + { + if ((renderInfo.CurrentRenderType & RenderType.Retractions) == RenderType.Retractions) + { + Vector3 position = new Vector3(this.position); + + if (renderInfo.CurrentRenderType.HasFlag(RenderType.HideExtruderOffsets)) + { + Vector2 offset = renderInfo.GetExtruderOffset(extruderIndex); + position = position + new Vector3(offset); + } + + RGBA_Bytes color = renderInfo.GetMaterialColor(extruderIndex + 1); + if (extruderIndex == 0) + { + if (extrusionAmount > 0) + { + color = RGBA_Bytes.Blue; + } + else + { + color = RGBA_Bytes.Red; + } + } + if (extrusionAmount > 0) + { + // unretraction + CreatePointer(colorVertexData, indexData, position + new Vector3(0, 0, 1.3), position + new Vector3(0, 0, .3), Radius(1), 5, color); + } + else + { + // retraction + CreatePointer(colorVertexData, indexData, position + new Vector3(0, 0, .3), position + new Vector3(0, 0, 1.3), Radius(1), 5, color); + } + } + } + + public override void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo) + { + if ((renderInfo.CurrentRenderType & RenderType.Retractions) == RenderType.Retractions) + { + double radius = Radius(renderInfo.LayerScale); + Vector2 position = new Vector2(this.position.x, this.position.y); + + if (renderInfo.CurrentRenderType.HasFlag(RenderType.HideExtruderOffsets)) + { + Vector2 offset = renderInfo.GetExtruderOffset(extruderIndex); + position = position + offset; + } + + renderInfo.Transform.transform(ref position); + + RGBA_Bytes retractionColor = new RGBA_Bytes(RGBA_Bytes.Red, 200); + if (extrusionAmount > 0) + { + // unretraction + retractionColor = new RGBA_Bytes(RGBA_Bytes.Blue, 200); + } + + // render the part using opengl + Graphics2DOpenGL graphics2DGl = graphics2D as Graphics2DOpenGL; + if (graphics2DGl != null) + { + graphics2DGl.DrawAACircle(position, radius, retractionColor); + } + else + { + Ellipse extrusion = new Ellipse(position, radius); + graphics2D.Render(extrusion, retractionColor); + } + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureTravel.cs b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureTravel.cs new file mode 100644 index 000000000..a64936234 --- /dev/null +++ b/MatterControl.OpenGL/GCodeRenderer/RenderFeatures/RenderFeatureTravel.cs @@ -0,0 +1,138 @@ +/* +Copyright (c) 2014, Lars Brubaker +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.Agg; +using MatterHackers.Agg.VertexSource; +using MatterHackers.RenderOpenGl; +using MatterHackers.VectorMath; + +namespace MatterHackers.GCodeVisualizer +{ + public class RenderFeatureTravel : RenderFeatureBase + { + protected Vector3Float start; + protected Vector3Float end; + protected float travelSpeed; + + protected Vector3Float GetStart(GCodeRenderInfo renderInfo) + { + if (renderInfo.CurrentRenderType.HasFlag(RenderType.HideExtruderOffsets)) + { + Vector3Float start = this.start; + Vector2 offset = renderInfo.GetExtruderOffset(extruderIndex); + start.x += (float)offset.x; + start.y += (float)offset.y; + return start; + } + + return this.start; + } + + protected Vector3Float GetEnd(GCodeRenderInfo renderInfo) + { + if (renderInfo.CurrentRenderType.HasFlag(RenderType.HideExtruderOffsets)) + { + Vector3Float end = this.end; + Vector2 offset = renderInfo.GetExtruderOffset(extruderIndex); + end.x += (float)offset.x; + end.y += (float)offset.y; + return end; + } + + return this.end; + } + + public RenderFeatureTravel(Vector3 start, Vector3 end, int extruderIndex, double travelSpeed) + : base(extruderIndex) + { + this.extruderIndex = extruderIndex; + this.start = new Vector3Float(start); + this.end = new Vector3Float(end); + this.travelSpeed = (float)travelSpeed; + } + + public override void CreateRender3DData(VectorPOD colorVertexData, VectorPOD indexData, GCodeRenderInfo renderInfo) + { + if ((renderInfo.CurrentRenderType & RenderType.Moves) == RenderType.Moves) + { + Vector3Float start = this.GetStart(renderInfo); + Vector3Float end = this.GetEnd(renderInfo); + CreateCylinder(colorVertexData, indexData, new Vector3(start), new Vector3(end), .1, 6, GCodeRenderer.TravelColor, .2); + } + } + + public override void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo) + { + if ((renderInfo.CurrentRenderType & RenderType.Moves) == RenderType.Moves) + { + double movementLineWidth = 0.35 * renderInfo.LayerScale; + RGBA_Bytes movementColor = new RGBA_Bytes(10, 190, 15); + + // render the part using opengl + Graphics2DOpenGL graphics2DGl = graphics2D as Graphics2DOpenGL; + if (graphics2DGl != null) + { + Vector3Float startF = this.GetStart(renderInfo); + Vector3Float endF = this.GetEnd(renderInfo); + Vector2 start = new Vector2(startF.x, startF.y); + renderInfo.Transform.transform(ref start); + + Vector2 end = new Vector2(endF.x, endF.y); + renderInfo.Transform.transform(ref end); + + graphics2DGl.DrawAALineRounded(start, end, movementLineWidth, movementColor); + } + else + { + PathStorage pathStorage = new PathStorage(); + VertexSourceApplyTransform transformedPathStorage = new VertexSourceApplyTransform(pathStorage, renderInfo.Transform); + Stroke stroke = new Stroke(transformedPathStorage, movementLineWidth); + + stroke.line_cap(LineCap.Round); + stroke.line_join(LineJoin.Round); + + Vector3Float start = this.GetStart(renderInfo); + Vector3Float end = this.GetEnd(renderInfo); + + pathStorage.Add(start.x, start.y, ShapePath.FlagsAndCommand.CommandMoveTo); + if (end.x != start.x || end.y != start.y) + { + pathStorage.Add(end.x, end.y, ShapePath.FlagsAndCommand.CommandLineTo); + } + else + { + pathStorage.Add(end.x + .01, end.y, ShapePath.FlagsAndCommand.CommandLineTo); + } + + graphics2D.Render(stroke, 0, movementColor); + } + } + } + } +} \ No newline at end of file diff --git a/MatterControl.OpenGL/MatterControl.OpenGL.csproj b/MatterControl.OpenGL/MatterControl.OpenGL.csproj new file mode 100644 index 000000000..a63356258 --- /dev/null +++ b/MatterControl.OpenGL/MatterControl.OpenGL.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268} + Library + Properties + MatterControl.OpenGL + MatterControl.OpenGL + v4.6.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + {97d5ade3-c1b4-4b46-8a3e-718a4f7f079f} + MatterControl.Printing + + + {657dbc6d-c3ea-4398-a3fa-ddb73c14f71b} + Agg + + + {74f6bb6c-9d02-4512-a59a-21940e35c532} + Gui + + + {545b6912-77ff-4b34-ba76-6c3d6a32be6a} + RenderOpenGl + + + {d3e41b4e-bfbb-44ca-94c8-95c00f754fdd} + VectorMath + + + + \ No newline at end of file diff --git a/MatterControl.OpenGL/Properties/AssemblyInfo.cs b/MatterControl.OpenGL/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..b8b769aee --- /dev/null +++ b/MatterControl.OpenGL/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MatterControl.OpenGL")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MatterControl.OpenGL")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cbdeec31-d688-417b-9bf2-f0db2e4fb268")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MatterControl.Printing/GCode/GCodeFile.cs b/MatterControl.Printing/GCode/GCodeFile.cs new file mode 100644 index 000000000..c3adbf72b --- /dev/null +++ b/MatterControl.Printing/GCode/GCodeFile.cs @@ -0,0 +1,254 @@ +/* +Copyright (c) 2016, Lars Brubaker +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.Agg; +using MatterHackers.VectorMath; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +namespace MatterHackers.GCodeVisualizer +{ + public abstract class GCodeFile + { + private static readonly Vector4 MaxAccelerationMmPerS2 = new Vector4(1000, 1000, 100, 5000); + private static readonly Vector4 MaxVelocityMmPerS = new Vector4(500, 500, 5, 25); + private static readonly Vector4 VelocitySameAsStopMmPerS = new Vector4(8, 8, .4, 5); + +#if __ANDROID__ + protected const int Max32BitFileSize = 10000000; // 10 megs +#else + protected const int Max32BitFileSize = 100000000; // 100 megs +#endif + + public static void AssertDebugNotDefined() + { +#if DEBUG + throw new Exception("DEBUG is defined and should not be!"); +#endif + } + + #region Abstract Functions + // the number of lines in the file + public abstract int LineCount { get; } + + public abstract int NumChangesInZ { get; } + public abstract double TotalSecondsInPrint { get; } + + public abstract void Clear(); + + public abstract RectangleDouble GetBounds(); + + public abstract double GetFilamentCubicMm(double filamentDiameter); + + public abstract double GetFilamentDiameter(); + + public abstract double GetFilamentUsedMm(double filamentDiameter); + + public abstract double GetFilamentWeightGrams(double filamentDiameterMm, double density); + + public abstract double GetFirstLayerHeight(); + + public abstract int GetInstructionIndexAtLayer(int layerIndex); + + public abstract double GetLayerHeight(); + + public abstract int GetLayerIndex(int instructionIndex); + + public abstract Vector2 GetWeightedCenter(); + + public abstract PrinterMachineInstruction Instruction(int i); + + public abstract bool IsExtruding(int instructionIndexToCheck); + public abstract double PercentComplete(int instructionIndex); + + public abstract double Ratio0to1IntoContainedLayer(int instructionIndex); + #endregion Abstract Functions + + #region Static Functions + + public static int CalculateChecksum(string commandToGetChecksumFor) + { + int checksum = 0; + if (commandToGetChecksumFor.Length > 0) + { + checksum = commandToGetChecksumFor[0]; + for (int i = 1; i < commandToGetChecksumFor.Length; i++) + { + checksum ^= commandToGetChecksumFor[i]; + } + } + return checksum; + } + + public static bool IsLayerChange(string lineString) + { + return lineString.StartsWith("; LAYER:") + || lineString.StartsWith(";LAYER:"); + } + + public static bool FileTooBigToLoad(string fileName) + { + if (File.Exists(fileName) + && Is32Bit) + { + FileInfo info = new FileInfo(fileName); + // Let's make sure we can load a file this big + if (info.Length > Max32BitFileSize) + { + // It is too big to load + return true; + } + } + + return false; + } + + public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref int readValue, int startIndex = 0, string stopCheckingString = ";") + { + double doubleValue = readValue; + if(GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref doubleValue, startIndex, stopCheckingString)) + { + readValue = (int)doubleValue; + return true; + } + + return false; + } + + public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref double readValue, int startIndex = 0, string stopCheckingString = ";") + { + int stringPos = stringWithNumber.IndexOf(stringToCheckAfter, startIndex); + int stopPos = stringWithNumber.IndexOf(stopCheckingString); + if (stringPos != -1 + && (stopPos == -1 || stringPos < stopPos)) + { + stringPos += stringToCheckAfter.Length; + readValue = agg_basics.ParseDouble(stringWithNumber, ref stringPos, true); + + return true; + } + + return false; + } + + public static bool GetFirstStringAfter(string stringToCheckAfter, string fullStringToLookIn, string separatorString, ref string nextString, int startIndex = 0) + { + int stringPos = fullStringToLookIn.IndexOf(stringToCheckAfter, startIndex); + if (stringPos != -1) + { + int separatorPos = fullStringToLookIn.IndexOf(separatorString, stringPos); + if (separatorPos != -1) + { + nextString = fullStringToLookIn.Substring(stringPos + stringToCheckAfter.Length, separatorPos - (stringPos + stringToCheckAfter.Length)); + return true; + } + } + + return false; + } + + public static GCodeFile Load(string fileName) + { + if (FileTooBigToLoad(fileName)) + { + return new GCodeFileStreamed(fileName); + } + else + { + return new GCodeFileLoaded(fileName); + } + } + + public static string ReplaceNumberAfter(char charToReplaceAfter, string stringWithNumber, double numberToPutIn) + { + int charPos = stringWithNumber.IndexOf(charToReplaceAfter); + if (charPos != -1) + { + int spacePos = stringWithNumber.IndexOf(" ", charPos); + if (spacePos == -1) + { + string newString = string.Format("{0}{1:0.#####}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn); + return newString; + } + else + { + string newString = string.Format("{0}{1:0.#####}{2}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn, stringWithNumber.Substring(spacePos)); + return newString; + } + } + + return stringWithNumber; + } + + protected static double GetSecondsThisLine(Vector3 deltaPositionThisLine, double deltaEPositionThisLine, double feedRateMmPerMin) + { + double startingVelocityMmPerS = VelocitySameAsStopMmPerS.x; + double endingVelocityMmPerS = VelocitySameAsStopMmPerS.x; + double maxVelocityMmPerS = Math.Min(feedRateMmPerMin / 60, MaxVelocityMmPerS.x); + double acceleration = MaxAccelerationMmPerS2.x; + double lengthOfThisMoveMm = Math.Max(deltaPositionThisLine.Length, deltaEPositionThisLine); + + double distanceToMaxVelocity = GetDistanceToReachEndingVelocity(startingVelocityMmPerS, maxVelocityMmPerS, acceleration); + if (distanceToMaxVelocity <= lengthOfThisMoveMm / 2) + { + // we will reach max velocity then run at it and then decelerate + double accelerationTime = GetTimeToAccelerateDistance(startingVelocityMmPerS, distanceToMaxVelocity, acceleration) * 2; + double runningTime = (lengthOfThisMoveMm - (distanceToMaxVelocity * 2)) / maxVelocityMmPerS; + return accelerationTime + runningTime; + } + else + { + // we will accelerate to the center then decelerate + double accelerationTime = GetTimeToAccelerateDistance(startingVelocityMmPerS, lengthOfThisMoveMm / 2, acceleration) * 2; + return accelerationTime; + } + } + + private static double GetDistanceToReachEndingVelocity(double startingVelocityMmPerS, double endingVelocityMmPerS, double accelerationMmPerS2) + { + double endingVelocityMmPerS2 = endingVelocityMmPerS * endingVelocityMmPerS; + double startingVelocityMmPerS2 = startingVelocityMmPerS * startingVelocityMmPerS; + return (endingVelocityMmPerS2 - startingVelocityMmPerS2) / (2.0 * accelerationMmPerS2); + } + + private static double GetTimeToAccelerateDistance(double startingVelocityMmPerS, double distanceMm, double accelerationMmPerS2) + { + // d = vi * t + .5 * a * t^2; + // t = (√(vi^2+2ad)-vi)/a + double startingVelocityMmPerS2 = startingVelocityMmPerS * startingVelocityMmPerS; + double distanceAcceleration2 = 2 * accelerationMmPerS2 * distanceMm; + return (Math.Sqrt(startingVelocityMmPerS2 + distanceAcceleration2) - startingVelocityMmPerS) / accelerationMmPerS2; + } + + private static readonly bool Is32Bit = IntPtr.Size == 4; + #endregion Static Functions + } +} \ No newline at end of file diff --git a/MatterControl.Printing/GCode/GCodeFileLoaded.cs b/MatterControl.Printing/GCode/GCodeFileLoaded.cs new file mode 100644 index 000000000..bd6840bc8 --- /dev/null +++ b/MatterControl.Printing/GCode/GCodeFileLoaded.cs @@ -0,0 +1,969 @@ +/* +Copyright (c) 2014, Lars Brubaker +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. +*/ +#define MULTI_THREAD +#define DUMP_SLOW_TIMES + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using MatterHackers.Agg; +using MatterHackers.VectorMath; + +namespace MatterHackers.GCodeVisualizer +{ + public class GCodeFileLoaded : GCodeFile + { + private double amountOfAccumulatedEWhileParsing = 0; + + private List indexOfChangeInZ = new List(); + private Vector2 center = Vector2.Zero; + private double parsingLastZ; + private bool gcodeHasExplicitLayerChangeInfo = false; + private double firstLayerThickness; + private double layerThickness; + + private double filamentUsedMmCache = 0; + private double diameterOfFilamentUsedMmCache = 0; + + private List GCodeCommandQueue = new List(); + + public GCodeFileLoaded(bool gcodeHasExplicitLayerChangeInfo = false) + { + this.gcodeHasExplicitLayerChangeInfo = gcodeHasExplicitLayerChangeInfo; + } + + public GCodeFileLoaded(string pathAndFileName, bool gcodeHasExplicitLayerChangeInfo = false) + { + this.gcodeHasExplicitLayerChangeInfo = gcodeHasExplicitLayerChangeInfo; + this.Load(pathAndFileName); + } + + public override PrinterMachineInstruction Instruction(int index) + { + return GCodeCommandQueue[index]; + } + + public override int LineCount => GCodeCommandQueue.Count; + + public override void Clear() + { + indexOfChangeInZ.Clear(); + GCodeCommandQueue.Clear(); + } + + public override double TotalSecondsInPrint => Instruction(0).secondsToEndFromHere; + + public void Add(PrinterMachineInstruction printerMachineInstruction) + { + Insert(LineCount, printerMachineInstruction); + } + + public void Insert(int insertIndex, PrinterMachineInstruction printerMachineInstruction) + { + for (int i = 0; i < indexOfChangeInZ.Count; i++) + { + if (insertIndex < indexOfChangeInZ[i]) + { + indexOfChangeInZ[i]++; + } + } + + GCodeCommandQueue.Insert(insertIndex, printerMachineInstruction); + } + + public static GCodeFile ParseGCodeString(string gcodeContents) + { + return ParseFileContents(gcodeContents, null); + } + + public static GCodeFileLoaded Load(Stream fileStream) + { + GCodeFileLoaded loadedGCode = null; + try + { + string gCodeString = ""; + using (var reader = new StreamReader(fileStream)) + { + gCodeString = reader.ReadToEnd(); + } + + loadedGCode = ParseFileContents(gCodeString, null); + } + catch (IOException e) + { + Debug.Print(e.Message); + } + + return loadedGCode; + } + + public static async Task LoadInBackground(string fileName, ReportProgressRatio progressReporter) + { + if (Path.GetExtension(fileName).ToUpper() == ".GCODE") + { + try + { + if (File.Exists(fileName) && !FileTooBigToLoad(fileName)) + { + return await Task.Run(() => + { + return ParseFileContents(File.ReadAllText(fileName), progressReporter); + }); + } + } + catch (IOException e) + { + Debug.Print(e.Message); + } + } + + return null; + } + + public new void Load(string gcodePathAndFileName) + { + if (!FileTooBigToLoad(gcodePathAndFileName)) + { + using (FileStream fileStream = new FileStream(gcodePathAndFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (StreamReader streamReader = new StreamReader(fileStream)) + { + GCodeFileLoaded loadedFile = GCodeFileLoaded.Load(streamReader.BaseStream); + + this.indexOfChangeInZ = loadedFile.indexOfChangeInZ; + this.center = loadedFile.center; + this.parsingLastZ = loadedFile.parsingLastZ; + + this.GCodeCommandQueue = loadedFile.GCodeCommandQueue; + } + } + } + } + + private static IEnumerable CustomSplit(string newtext, char splitChar) + { + int endOfLastFind = 0; + int positionOfSplitChar = newtext.IndexOf(splitChar); + while (positionOfSplitChar != -1) + { + string text = newtext.Substring(endOfLastFind, positionOfSplitChar - endOfLastFind).Trim(); + yield return text; + endOfLastFind = positionOfSplitChar + 1; + positionOfSplitChar = newtext.IndexOf(splitChar, endOfLastFind); + } + + string lastText = newtext.Substring(endOfLastFind); + yield return lastText; + } + + private static int CountNumLines(string gCodeString) + { + int crCount = 0; + foreach (char testCharacter in gCodeString) + { + if (testCharacter == '\n') + { + crCount++; + } + } + + return crCount + 1; + } + + public static GCodeFileLoaded ParseFileContents(string gCodeString, ReportProgressRatio progressReporter) + { + if (gCodeString == null) + { + return null; + } + + Stopwatch loadTime = Stopwatch.StartNew(); + + Stopwatch maxProgressReport = new Stopwatch(); + maxProgressReport.Start(); + PrinterMachineInstruction machineInstructionForLine = new PrinterMachineInstruction("None"); + + bool gcodeHasExplicitLayerChangeInfo = false; + if (gCodeString.Contains("LAYER:")) + { + gcodeHasExplicitLayerChangeInfo = true; + } + + GCodeFileLoaded loadedGCodeFile = new GCodeFileLoaded(gcodeHasExplicitLayerChangeInfo); + + int crCount = CountNumLines(gCodeString); + int lineIndex = 0; + foreach (string outputString in CustomSplit(gCodeString, '\n')) + { + string lineString = outputString.Trim(); + machineInstructionForLine = new PrinterMachineInstruction(lineString, machineInstructionForLine, false); + + if (lineString.Length > 0) + { + switch (lineString[0]) + { + case 'G': + loadedGCodeFile.ParseGLine(lineString, machineInstructionForLine); + break; + + case 'M': + loadedGCodeFile.ParseMLine(lineString, machineInstructionForLine); + break; + + case 'T': + double extruderIndex = 0; + if (GetFirstNumberAfter("T", lineString, ref extruderIndex)) + { + machineInstructionForLine.ExtruderIndex = (int)extruderIndex; + } + break; + + case ';': + if (gcodeHasExplicitLayerChangeInfo && IsLayerChange(lineString)) + { + loadedGCodeFile.IndexOfChangeInZ.Add(loadedGCodeFile.GCodeCommandQueue.Count); + } + if (lineString.StartsWith("; layerThickness")) + { + loadedGCodeFile.layerThickness = double.Parse(lineString.Split('=')[1]); + } + else if (lineString.StartsWith("; firstLayerThickness") && loadedGCodeFile.firstLayerThickness == 0) + { + loadedGCodeFile.firstLayerThickness = double.Parse(lineString.Split('=')[1]); + } + break; + + case '@': + break; + + default: +#if DEBUG + throw new NotImplementedException(); +#else + break; +#endif + } + } + + loadedGCodeFile.GCodeCommandQueue.Add(machineInstructionForLine); + + if (progressReporter != null && maxProgressReport.ElapsedMilliseconds > 200) + { + progressReporter((double)lineIndex / crCount / 2, "", out bool continueProcessing); + if (!continueProcessing) + { + return null; + } + + maxProgressReport.Restart(); + } + + lineIndex++; + } + + loadedGCodeFile.AnalyzeGCodeLines(progressReporter); + + loadTime.Stop(); + Console.WriteLine("Time To Load Seconds: {0:0.00}".FormatWith(loadTime.Elapsed.TotalSeconds)); + + return loadedGCodeFile; + } + + private void AnalyzeGCodeLines(ReportProgressRatio progressReporter) + { + double feedRateMmPerMin = 0; + Vector3 lastPrinterPosition = new Vector3(); + double lastEPosition = 0; + + Stopwatch maxProgressReport = new Stopwatch(); + maxProgressReport.Start(); + + for (int lineIndex = 0; lineIndex < GCodeCommandQueue.Count; lineIndex++) + { + PrinterMachineInstruction instruction = GCodeCommandQueue[lineIndex]; + string line = instruction.Line; + Vector3 deltaPositionThisLine = new Vector3(); + double deltaEPositionThisLine = 0; + string lineToParse = line.ToUpper().Trim(); + if (lineToParse.StartsWith("G0") || lineToParse.StartsWith("G1")) + { + double newFeedRateMmPerMin = 0; + if (GetFirstNumberAfter("F", lineToParse, ref newFeedRateMmPerMin)) + { + feedRateMmPerMin = newFeedRateMmPerMin; + } + + Vector3 attemptedDestination = lastPrinterPosition; + GetFirstNumberAfter("X", lineToParse, ref attemptedDestination.x); + GetFirstNumberAfter("Y", lineToParse, ref attemptedDestination.y); + GetFirstNumberAfter("Z", lineToParse, ref attemptedDestination.z); + + double ePosition = lastEPosition; + GetFirstNumberAfter("E", lineToParse, ref ePosition); + + deltaPositionThisLine = attemptedDestination - lastPrinterPosition; + deltaEPositionThisLine = Math.Abs(ePosition - lastEPosition); + + lastPrinterPosition = attemptedDestination; + lastEPosition = ePosition; + } + else if (lineToParse.StartsWith("G92")) + { + double ePosition = 0; + if (GetFirstNumberAfter("E", lineToParse, ref ePosition)) + { + lastEPosition = ePosition; + } + } + + if (feedRateMmPerMin > 0) + { + instruction.secondsThisLine = (float)GetSecondsThisLine(deltaPositionThisLine, deltaEPositionThisLine, feedRateMmPerMin); + } + + if (progressReporter != null && maxProgressReport.ElapsedMilliseconds > 200) + { + progressReporter(((double) lineIndex / GCodeCommandQueue.Count / 2) + .5, "", out bool continueProcessing); + if (!continueProcessing) + { + return; + } + + maxProgressReport.Restart(); + } + } + + double accumulatedTime = 0; + for (int i = GCodeCommandQueue.Count - 1; i >= 0; i--) + { + PrinterMachineInstruction line = GCodeCommandQueue[i]; + accumulatedTime += line.secondsThisLine; + line.secondsToEndFromHere = (float)accumulatedTime; + } + } + + public Vector2 Center + { + get { return center; } + } + + public override double PercentComplete(int instructionIndex) + { + if (GCodeCommandQueue.Count > 0) + { + return Math.Min(99.9, (double)instructionIndex / (double)GCodeCommandQueue.Count * 100); + } + + return 100; + } + + public override int GetInstructionIndexAtLayer(int layerIndex) + { + return IndexOfChangeInZ[layerIndex]; + } + + private List IndexOfChangeInZ + { + get { return indexOfChangeInZ; } + } + + public override int NumChangesInZ + { + get { return indexOfChangeInZ.Count; } + } + + private void ParseMLine(string lineString, PrinterMachineInstruction processingMachineState) + { + // take off any comments before we check its length + int commentIndex = lineString.IndexOf(';'); + if (commentIndex != -1) + { + lineString = lineString.Substring(0, commentIndex); + } + + string[] splitOnSpace = lineString.Split(' '); + switch (splitOnSpace[0].Substring(1).Trim()) + { + case "01": + // show a message? + break; + + case "6": + // wait for tool to heat up (wait for condition?) + break; + + case "101": + // extrude on, forward + break; + + case "18": + // turn off stepers + break; + + case "42": + // Stop on material exhausted / Switch I/O pin + break; + + case "72": + // makerbot, Play tone or song + break; + + case "73": + // makerbot, Manually set build percentage + break; + + case "82": + // set extruder to absolute mode + break; + + case "83": + //Set extruder to relative mode + break; + + case "84": + // lineString = "M84 ; disable motors\r" + break; + + case "92": + // set steps per mm + break; + + case "102": + // extrude on reverse + break; + + case "103": + // extrude off + break; + + case "104": + // set extruder tempreature + break; + + case "105": + // M105 Custom code for temperature reading. (Not used) + break; + + case "106": + // turn fan on + break; + + case "107": + // turn fan off + break; + + case "108": + // set extruder speed + break; + + case "109": + // set heated platform temperature + break; + + case "114": + break; + + case "117": + // in Marlin: Display Message + break; + + case "126": + // enable fan (makerbot) + break; + + case "127": + // disable fan (makerbot) + break; + + case "132": + // recall stored home offsets for axis xyzab + break; + + case "133": + // MakerBot wait for toolhead to heat + break; + + case "134": + // MakerBot wait for platform to reach target temp + break; + + case "135": + // MakerBot change toolhead + break; + + case "140": + // set bed temperature + break; + + case "190": + // wait for bed temperature to be reached + break; + + case "200": + // M200 sets the filament diameter. + break; + + case "201": + // set axis acceleration + break; + + case "204": // - Set default acceleration + break; + + case "207": // M207: calibrate z axis by detecting z max length + break; + + case "208": // M208: set axis max travel + break; + + case "209": // M209: enable automatic retract + break; + + case "210": // Set homing rate + break; + + case "226": // user request pause + break; + + case "227": // Enable Automatic Reverse and Prime + break; + + case "301": + break; + + case "400": // Wait for current moves to finish + break; + + case "565": // M565: Set Z probe offset + break; + case "1200"://M1200 Makerbot Fake gCode command for start build notification + break; + case "1201"://M1201 Makerbot Fake gCode command for end build notification + break; + case "1202"://M1202 Makerbot Fake gCode command for reset board + break; + + default: + break; + } + } + + private void ParseGLine(string lineString, PrinterMachineInstruction processingMachineState) + { + // take off any comments before we check its length + int commentIndex = lineString.IndexOf(';'); + if (commentIndex != -1) + { + lineString = lineString.Substring(0, commentIndex); + } + + string[] splitOnSpace = lineString.Split(' '); + string onlyNumber = splitOnSpace[0].Substring(1).Trim(); + switch (onlyNumber) + { + case "0": + goto case "1"; + + case "4": + case "04": + // wait a given number of miliseconds + break; + + case "1": + // get the x y z to move to + { + double valueX = 0; + if (GCodeFile.GetFirstNumberAfter("X", lineString, ref valueX)) + { + processingMachineState.X = valueX; + } + double valueY = 0; + if (GCodeFile.GetFirstNumberAfter("Y", lineString, ref valueY)) + { + processingMachineState.Y = valueY; + } + double valueZ = 0; + if (GCodeFile.GetFirstNumberAfter("Z", lineString, ref valueZ)) + { + processingMachineState.Z = valueZ; + } + double valueE = 0; + if (GCodeFile.GetFirstNumberAfter("E", lineString, ref valueE)) + { + if (processingMachineState.movementType == PrinterMachineInstruction.MovementTypes.Absolute) + { + processingMachineState.EPosition = valueE + amountOfAccumulatedEWhileParsing; + } + else + { + processingMachineState.EPosition += valueE; + } + } + double valueF = 0; + if (GCodeFile.GetFirstNumberAfter("F", lineString, ref valueF)) + { + processingMachineState.FeedRate = valueF; + } + } + + if (!gcodeHasExplicitLayerChangeInfo) + { + if (processingMachineState.Z != parsingLastZ || indexOfChangeInZ.Count == 0) + { + // if we changed z or there is a movement and we have never started a layer index + indexOfChangeInZ.Add(GCodeCommandQueue.Count); + } + } + parsingLastZ = processingMachineState.Position.z; + break; + + case "10": // firmware retract + break; + + case "11": // firmware unretract + break; + + case "21": + // set to metric + break; + + case "28": + // G28 Return to home position (machine zero, aka machine reference point) + break; + + case "29": + // G29 Probe the z-bed in 3 places + break; + + case "30": + // G30 Probe z in current position + break; + + case "90": // G90 is Absolute Distance Mode + processingMachineState.movementType = PrinterMachineInstruction.MovementTypes.Absolute; + break; + + case "91": // G91 is Incremental Distance Mode + processingMachineState.movementType = PrinterMachineInstruction.MovementTypes.Relative; + break; + + case "92": + // set current head position values (used to reset origin) + double ePosition = 0; + if (GetFirstNumberAfter("E", lineString, ref ePosition)) + { + // remember how much e position we just gave up + amountOfAccumulatedEWhileParsing = (processingMachineState.EPosition - ePosition); + } + break; + + case "130": + //Set Digital Potentiometer value + break; + + case "161": + // home x,y axis minimum + break; + + case "162": + // home z axis maximum + break; + + default: + break; + } + } + + public override Vector2 GetWeightedCenter() + { + Vector2 total = new Vector2(); +#if !MULTI_THREAD + foreach (PrinterMachineInstruction state in GCodeCommandQueue) + { + total += new Vector2(state.Position.x, state.Position.y); + } +#else + Parallel.For( + 0, + GCodeCommandQueue.Count, + () => new Vector2(), + (int index, ParallelLoopState loop, Vector2 subtotal) => + { + PrinterMachineInstruction state = GCodeCommandQueue[index]; + subtotal += new Vector2(state.Position.x, state.Position.y); + return subtotal; + }, + (x) => + { + total += new Vector2(x.x, x.y); + } + ); +#endif + + return total / GCodeCommandQueue.Count; + } + + public override RectangleDouble GetBounds() + { + RectangleDouble bounds = new RectangleDouble(double.MaxValue, double.MaxValue, double.MinValue, double.MinValue); +#if !MULTI_THREAD + foreach (PrinterMachineInstruction state in GCodeCommandQueue) + { + bounds.Left = Math.Min(state.Position.x, bounds.Left); + bounds.Right = Math.Max(state.Position.x, bounds.Right); + bounds.Bottom = Math.Min(state.Position.y, bounds.Bottom); + bounds.Top = Math.Max(state.Position.y, bounds.Top); + } +#else + Parallel.For( + 0, + GCodeCommandQueue.Count, + () => new RectangleDouble(double.MaxValue, double.MaxValue, double.MinValue, double.MinValue), + (int index, ParallelLoopState loop, RectangleDouble subtotal) => + { + PrinterMachineInstruction state = GCodeCommandQueue[index]; + subtotal.Left = Math.Min(state.Position.x, subtotal.Left); + subtotal.Right = Math.Max(state.Position.x, subtotal.Right); + subtotal.Bottom = Math.Min(state.Position.y, subtotal.Bottom); + subtotal.Top = Math.Max(state.Position.y, subtotal.Top); + + return subtotal; + }, + (x) => + { + bounds.Left = Math.Min(x.Left, bounds.Left); + bounds.Right = Math.Max(x.Right, bounds.Right); + bounds.Bottom = Math.Min(x.Bottom, bounds.Bottom); + bounds.Top = Math.Max(x.Top, bounds.Top); + } + ); +#endif + return bounds; + } + + public override bool IsExtruding(int instructionIndexToCheck) + { + if (instructionIndexToCheck > 1 && instructionIndexToCheck < GCodeCommandQueue.Count) + { + double extrusionLengeth = GCodeCommandQueue[instructionIndexToCheck].EPosition - GCodeCommandQueue[instructionIndexToCheck - 1].EPosition; + if (extrusionLengeth > 0) + { + return true; + } + } + + return false; + } + + public override double GetFilamentUsedMm(double filamentDiameter) + { + if (filamentUsedMmCache == 0 || filamentDiameter != diameterOfFilamentUsedMmCache) + { + double lastEPosition = 0; + double filamentMm = 0; + for (int i = 0; i < GCodeCommandQueue.Count; i++) + { + PrinterMachineInstruction instruction = GCodeCommandQueue[i]; + //filamentMm += instruction.EPosition; + + string lineToParse = instruction.Line; + if (lineToParse.StartsWith("G0") || lineToParse.StartsWith("G1")) + { + double ePosition = lastEPosition; + if (GetFirstNumberAfter("E", lineToParse, ref ePosition)) + { + if (instruction.movementType == PrinterMachineInstruction.MovementTypes.Absolute) + { + double deltaEPosition = ePosition - lastEPosition; + filamentMm += deltaEPosition; + } + else + { + filamentMm += ePosition; + } + + lastEPosition = ePosition; + } + } + else if (lineToParse.StartsWith("G92")) + { + double ePosition = 0; + if (GetFirstNumberAfter("E", lineToParse, ref ePosition)) + { + lastEPosition = ePosition; + } + } + } + + filamentUsedMmCache = filamentMm; + diameterOfFilamentUsedMmCache = filamentDiameter; + } + + return filamentUsedMmCache; + } + + public override double GetFilamentCubicMm(double filamentDiameterMm) + { + double filamentUsedMm = GetFilamentUsedMm(filamentDiameterMm); + double filamentRadius = filamentDiameterMm / 2; + double areaSquareMm = (filamentRadius * filamentRadius) * Math.PI; + + return areaSquareMm * filamentUsedMm; + } + + public override double GetFilamentWeightGrams(double filamentDiameterMm, double densityGramsPerCubicCm) + { + double cubicMmPerCubicCm = 1000; + double gramsPerCubicMm = densityGramsPerCubicCm / cubicMmPerCubicCm; + double cubicMms = GetFilamentCubicMm(filamentDiameterMm); + return cubicMms * gramsPerCubicMm; + } + + public void Save(string dest) + { + using (StreamWriter file = new StreamWriter(dest)) + { + foreach (PrinterMachineInstruction instruction in GCodeCommandQueue) + { + file.WriteLine(instruction.Line); + } + } + } + + double filamentDiameterCache = 0; + public override double GetFilamentDiameter() + { + if (filamentDiameterCache == 0) + { + for (int i = 0; i < Math.Min(20, GCodeCommandQueue.Count); i++) + { + if (GetFirstNumberAfter("filamentDiameter =", GCodeCommandQueue[i].Line, ref filamentDiameterCache)) + { + break; + } + } + + if (filamentDiameterCache == 0) + { + // didn't find it, so look at the end of the file for filament_diameter = + string lookFor = "; filament_diameter =";// 2.85 + for (int i = GCodeCommandQueue.Count - 1; i > Math.Max(0, GCodeCommandQueue.Count - 100); i--) + { + if (GetFirstNumberAfter(lookFor, GCodeCommandQueue[i].Line, ref filamentDiameterCache)) + { + } + } + } + + if(filamentDiameterCache == 0) + { + // it is still 0 so set it to something so we render + filamentDiameterCache = 1.75; + } + } + + return filamentDiameterCache; + } + + public override double GetLayerHeight() + { + if (layerThickness > 0) + { + return layerThickness; + } + + if (indexOfChangeInZ.Count > 2) + { + return GCodeCommandQueue[IndexOfChangeInZ[2]].Z - GCodeCommandQueue[IndexOfChangeInZ[1]].Z; + } + + return .5; + } + + public override double GetFirstLayerHeight() + { + if (firstLayerThickness > 0) + { + return firstLayerThickness; + } + + if (indexOfChangeInZ.Count > 1) + { + return GCodeCommandQueue[IndexOfChangeInZ[1]].Z - GCodeCommandQueue[IndexOfChangeInZ[0]].Z; + } + + return .5; + } + + public override int GetLayerIndex(int instructionIndex) + { + if (instructionIndex >= 0 + && instructionIndex < LineCount) + { + for (int zIndex = 0; zIndex < NumChangesInZ; zIndex++) + { + if (instructionIndex < IndexOfChangeInZ[zIndex]) + { + return zIndex; + } + } + + return NumChangesInZ - 1; + } + + return -1; + } + + public override double Ratio0to1IntoContainedLayer(int instructionIndex) + { + int currentLayer = GetLayerIndex(instructionIndex); + + if (currentLayer > -1) + { + int startIndex = 0; + if (currentLayer > 0) + { + startIndex = IndexOfChangeInZ[currentLayer - 1]; + } + int endIndex = LineCount - 1; + if (currentLayer < NumChangesInZ - 1) + { + endIndex = IndexOfChangeInZ[currentLayer]; + } + + int deltaFromStart = Math.Max(0, instructionIndex - startIndex); + return deltaFromStart / (double)(endIndex - startIndex); + } + + return 0; + } + } +} \ No newline at end of file diff --git a/MatterControl.Printing/GCode/GCodeFileStreamed.cs b/MatterControl.Printing/GCode/GCodeFileStreamed.cs new file mode 100644 index 000000000..9e6017a6b --- /dev/null +++ b/MatterControl.Printing/GCode/GCodeFileStreamed.cs @@ -0,0 +1,286 @@ +/* +Copyright (c) 2014, Lars Brubaker +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. +*/ +#define MULTI_THREAD + +using MatterHackers.Agg; +using MatterHackers.VectorMath; +using System; +using System.IO; + +namespace MatterHackers.GCodeVisualizer +{ + public class GCodeFileStreamed : GCodeFile + { + private StreamReader openGcodeStream; + object locker = new object(); + + private bool readLastLineOfFile = false; + private int readLineCount = 0; + private const int MaxLinesToBuffer = 128; + private PrinterMachineInstruction[] readLinesRingBuffer = new PrinterMachineInstruction[MaxLinesToBuffer]; + + public GCodeFileStreamed(string fileName) + { + var inStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + openGcodeStream = new StreamReader(inStream); + } + + ~GCodeFileStreamed() + { + CloseStream(); + } + + private void CloseStream() + { + if (openGcodeStream != null) + { + openGcodeStream.Close(); + openGcodeStream = null; + } + } + + public override int LineCount + { + get + { + if (openGcodeStream != null + && !readLastLineOfFile) + { + return Math.Max(readLineCount + 1, (int)(openGcodeStream.BaseStream.Length / 14)); + } + + return readLineCount; + } + } + + public long ByteCount + { + get + { + if (openGcodeStream != null + && !readLastLineOfFile) + { + return openGcodeStream.BaseStream.Length; + } + + return 0; + } + } + + public long BytePosition + { + get + { + if (openGcodeStream != null + && !readLastLineOfFile) + { + return openGcodeStream.BaseStream.Position; + } + + return 0; + } + } + + public override double TotalSecondsInPrint + { + get + { + // We don't know, so we always return -1 used to identify as streaming. + return -1; + } + } + + public override void Clear() + { + CloseStream(); + + readLastLineOfFile = false; + readLineCount = 0; + } + + public override Vector2 GetWeightedCenter() + { + throw new NotImplementedException("A streamed GCode file should not need to do this. Please validate the code that is calling this."); + } + + public override RectangleDouble GetBounds() + { + throw new NotImplementedException("A streamed GCode file should not need to do this. Please validate the code that is calling this."); + } + + public override double GetFilamentCubicMm(double filamentDiameter) + { + throw new NotImplementedException("A streamed GCode file should not need to do this. Please validate the code that is calling this."); + } + + public override bool IsExtruding(int instructionIndexToCheck) + { + throw new NotImplementedException(); + } + + public override double GetLayerHeight() + { + throw new NotImplementedException(); + } + + public override double GetFirstLayerHeight() + { + throw new NotImplementedException(); + } + + public override double GetFilamentUsedMm(double filamentDiameter) + { + throw new NotImplementedException(); + } + + public override double PercentComplete(int instructionIndex) + { + lock(locker) + { + if (openGcodeStream != null + && openGcodeStream.BaseStream.Length > 0) + { + return (double)openGcodeStream.BaseStream.Position / (double)openGcodeStream.BaseStream.Length * 100.0; + } + } + + return 100; + } + + public override int GetInstructionIndexAtLayer(int layerIndex) + { + return 0; + } + + public override double GetFilamentDiameter() + { + return 0; + } + + public override double GetFilamentWeightGrams(double filamentDiameterMm, double density) + { + return 0; + } + + public override int GetLayerIndex(int instructionIndex) + { + return 0; + } + + public override int NumChangesInZ + { + get + { + return 0; + } + } + + private double feedRateMmPerMin = 0; + private Vector3 lastPrinterPosition = new Vector3(); + private double lastEPosition = 0; + + public override PrinterMachineInstruction Instruction(int index) + { + lock(locker) + { + if (index < readLineCount - MaxLinesToBuffer) + { + throw new Exception("You are asking for a line we no longer have buffered"); + } + + while (index >= readLineCount) + { + string line = openGcodeStream.ReadLine(); + if (line == null) + { + readLastLineOfFile = true; + line = ""; + } + + int ringBufferIndex = readLineCount % MaxLinesToBuffer; + readLinesRingBuffer[ringBufferIndex] = new PrinterMachineInstruction(line); + + PrinterMachineInstruction instruction = readLinesRingBuffer[ringBufferIndex]; + Vector3 deltaPositionThisLine = new Vector3(); + double deltaEPositionThisLine = 0; + string lineToParse = line.ToUpper().Trim(); + if (lineToParse.StartsWith("G0") || lineToParse.StartsWith("G1")) + { + double newFeedRateMmPerMin = 0; + if (GetFirstNumberAfter("F", lineToParse, ref newFeedRateMmPerMin)) + { + feedRateMmPerMin = newFeedRateMmPerMin; + } + + Vector3 attemptedDestination = lastPrinterPosition; + GetFirstNumberAfter("X", lineToParse, ref attemptedDestination.x); + GetFirstNumberAfter("Y", lineToParse, ref attemptedDestination.y); + GetFirstNumberAfter("Z", lineToParse, ref attemptedDestination.z); + + double ePosition = lastEPosition; + GetFirstNumberAfter("E", lineToParse, ref ePosition); + + deltaPositionThisLine = attemptedDestination - lastPrinterPosition; + deltaEPositionThisLine = Math.Abs(ePosition - lastEPosition); + + lastPrinterPosition = attemptedDestination; + lastEPosition = ePosition; + } + else if (lineToParse.StartsWith("G92")) + { + double ePosition = 0; + if (GetFirstNumberAfter("E", lineToParse, ref ePosition)) + { + lastEPosition = ePosition; + } + } + + if (feedRateMmPerMin > 0) + { + instruction.secondsThisLine = (float)GetSecondsThisLine(deltaPositionThisLine, deltaEPositionThisLine, feedRateMmPerMin); + } + + readLineCount++; + } + } + + return readLinesRingBuffer[index % MaxLinesToBuffer]; + } + + public override double Ratio0to1IntoContainedLayer(int instructionIndex) + { + if (ByteCount != 0) + { + return BytePosition / (double)ByteCount; + } + + return 1; + } + } +} diff --git a/MatterControl.Printing/GCode/PrinterMachineInstruction.cs b/MatterControl.Printing/GCode/PrinterMachineInstruction.cs new file mode 100644 index 000000000..a9565df47 --- /dev/null +++ b/MatterControl.Printing/GCode/PrinterMachineInstruction.cs @@ -0,0 +1,152 @@ +/* +Copyright (c) 2014, Lars Brubaker +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.VectorMath; +using System.Text; + +namespace MatterHackers.GCodeVisualizer +{ + public class PrinterMachineInstruction + { + public byte[] byteLine; + + public string Line + { + get + { + return Encoding.Default.GetString(byteLine); + } + + set + { + byteLine = Encoding.Default.GetBytes(value); + } + } + + private Vector3Float xyzPosition = new Vector3Float(); + private float ePosition = 0; + private float feedRate = 0; + + public enum MovementTypes { Absolute, Relative }; + + // Absolute is the RepRap default + public MovementTypes movementType = MovementTypes.Absolute; + + public float secondsThisLine; + public float secondsToEndFromHere; + public bool clientInsertion; + + public PrinterMachineInstruction(string Line) + { + this.Line = Line; + } + + public PrinterMachineInstruction(string Line, PrinterMachineInstruction copy, bool clientInsertion = false) + : this(Line) + { + xyzPosition = copy.xyzPosition; + feedRate = copy.feedRate; + ePosition = copy.ePosition; + movementType = copy.movementType; + secondsToEndFromHere = copy.secondsToEndFromHere; + ExtruderIndex = copy.ExtruderIndex; + this.clientInsertion = clientInsertion; + } + + public int ExtruderIndex { get; set; } + + public Vector3 Position + { + get { return new Vector3(xyzPosition); } + } + + public double X + { + get { return xyzPosition.x; } + set + { + if (movementType == MovementTypes.Absolute) + { + xyzPosition.x = (float)value; + } + else + { + xyzPosition.x += (float)value; + } + } + } + + public double Y + { + get { return xyzPosition.y; } + set + { + if (movementType == MovementTypes.Absolute) + { + xyzPosition.y = (float)value; + } + else + { + xyzPosition.y += (float)value; + } + } + } + + public double Z + { + get { return xyzPosition.z; } + set + { + if (movementType == MovementTypes.Absolute) + { + xyzPosition.z = (float)value; + } + else + { + xyzPosition.z += (float)value; + } + } + } + + public double EPosition + { + get { return ePosition; } + set + { + ePosition = (float)value; + } + } + + public double FeedRate + { + get { return feedRate; } + set { feedRate = (float)value; } + } + } +} \ No newline at end of file diff --git a/MatterControl.Printing/MatterControl.Printing.csproj b/MatterControl.Printing/MatterControl.Printing.csproj new file mode 100644 index 000000000..da9ab2ae4 --- /dev/null +++ b/MatterControl.Printing/MatterControl.Printing.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F} + Library + Properties + MatterControl.Printing + MatterControl.Printing + v4.6.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {657dbc6d-c3ea-4398-a3fa-ddb73c14f71b} + Agg + + + {d3e41b4e-bfbb-44ca-94c8-95c00f754fdd} + VectorMath + + + + \ No newline at end of file diff --git a/MatterControl.Printing/Properties/AssemblyInfo.cs b/MatterControl.Printing/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..bdf9b5fb5 --- /dev/null +++ b/MatterControl.Printing/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MatterControl.Printing")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MatterControl.Printing")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("97d5ade3-c1b4-4b46-8a3e-718a4f7f079f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MatterControl.csproj b/MatterControl.csproj index 1d2d4dd6e..05bf214cb 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -209,6 +209,9 @@ + + + @@ -474,6 +477,14 @@ + + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268} + MatterControl.OpenGL + + + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F} + MatterControl.Printing + {9B062971-A88E-4A3D-B3C9-12B78D15FA66} clipper_library @@ -518,10 +529,6 @@ {D3E41B4E-BFBB-44CA-94C8-95C00F754FDD} VectorMath - - {F67AE800-B0C7-42A8-836F-597B4E74591C} - GCodeVisualizer - {D3ABF72C-64C2-4E51-A119-E077210FA990} SerialPortCommunication @@ -534,10 +541,6 @@ {C46CA728-DD2F-4DD1-971A-AAA89D9DFF95} MatterSlice - - {A737BC76-165B-46C6-82B7-8871C7C92942} - MeshViewer - {AE37DE1F-22F7-49EE-8732-FC6BC8DC58D9} Tesselate diff --git a/PartPreviewWindow/View3D/InteractionVolume.cs b/PartPreviewWindow/View3D/InteractionVolume.cs new file mode 100644 index 000000000..a94f51895 --- /dev/null +++ b/PartPreviewWindow/View3D/InteractionVolume.cs @@ -0,0 +1,206 @@ +/* +Copyright (c) 2014, Lars Brubaker +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 MatterHackers.Agg; +using MatterHackers.Agg.Font; +using MatterHackers.Agg.Image; +using MatterHackers.Agg.Transform; +using MatterHackers.Agg.VertexSource; +using MatterHackers.DataConverters3D; +using MatterHackers.RayTracer; +using MatterHackers.RenderOpenGl; +using MatterHackers.VectorMath; + +namespace MatterHackers.MeshVisualizer +{ + [Flags] + public enum LineArrows { None = 0, Start = 1, End = 2, Both = 3 }; + + public class InteractionVolume + { + public bool MouseDownOnControl; + public Matrix4X4 TotalTransform = Matrix4X4.Identity; + + private bool mouseOver = false; + + public InteractionVolume(IPrimitive collisionVolume, MeshViewerWidget meshViewerToDrawWith) + { + this.CollisionVolume = collisionVolume; + this.MeshViewerToDrawWith = meshViewerToDrawWith; + } + + public IPrimitive CollisionVolume { get; set; } + + public bool DrawOnTop { get; protected set; } + + protected MeshViewerWidget MeshViewerToDrawWith { get; } + + public IntersectInfo MouseMoveInfo { get; set; } + + public bool MouseOver + { + get + { + return mouseOver; + } + + set + { + if (mouseOver != value) + { + mouseOver = value; + Invalidate(); + } + } + } + + public static Vector3 SetBottomControlHeight(AxisAlignedBoundingBox originalSelectedBounds, Vector3 cornerPosition) + { + if (originalSelectedBounds.minXYZ.z < 0) + { + if (originalSelectedBounds.maxXYZ.z < 0) + { + cornerPosition.z = originalSelectedBounds.maxXYZ.z; + } + else + { + cornerPosition.z = 0; + } + } + + return cornerPosition; + } + + public static void DrawMeasureLine(Graphics2D graphics2D, Vector2 lineStart, Vector2 lineEnd, RGBA_Bytes color, LineArrows arrows) + { + graphics2D.Line(lineStart, lineEnd, RGBA_Bytes.Black); + + Vector2 direction = lineEnd - lineStart; + if (direction.LengthSquared > 0 + && (arrows.HasFlag(LineArrows.Start) || arrows.HasFlag(LineArrows.End))) + { + PathStorage arrow = new PathStorage(); + arrow.MoveTo(-3, -5); + arrow.LineTo(0, 0); + arrow.LineTo(3, -5); + if (arrows.HasFlag(LineArrows.End)) + { + double rotation = Math.Atan2(direction.y, direction.x); + IVertexSource correctRotation = new VertexSourceApplyTransform(arrow, Affine.NewRotation(rotation - MathHelper.Tau / 4)); + IVertexSource inPosition = new VertexSourceApplyTransform(correctRotation, Affine.NewTranslation(lineEnd)); + graphics2D.Render(inPosition, RGBA_Bytes.Black); + } + if (arrows.HasFlag(LineArrows.Start)) + { + double rotation = Math.Atan2(direction.y, direction.x) + MathHelper.Tau / 2; + IVertexSource correctRotation = new VertexSourceApplyTransform(arrow, Affine.NewRotation(rotation - MathHelper.Tau / 4)); + IVertexSource inPosition = new VertexSourceApplyTransform(correctRotation, Affine.NewTranslation(lineStart)); + graphics2D.Render(inPosition, RGBA_Bytes.Black); + } + } + } + + public virtual void Draw2DContent(Agg.Graphics2D graphics2D) + { + } + + public virtual void DrawGlContent(EventArgs e) + { + } + + public void Invalidate() + { + MeshViewerToDrawWith.Invalidate(); + } + + public virtual void OnMouseDown(MouseEvent3DArgs mouseEvent3D) + { + MouseDownOnControl = true; + MeshViewerToDrawWith.Invalidate(); + } + + public virtual void OnMouseMove(MouseEvent3DArgs mouseEvent3D) + { + } + + public virtual void OnMouseUp(MouseEvent3DArgs mouseEvent3D) + { + MouseDownOnControl = false; + } + + public virtual void SetPosition(IObject3D selectedItem) + { + } + } + + public class ValueDisplayInfo + { + private string formatString; + private string measureDisplayedString = ""; + private ImageBuffer measureDisplayImage = null; + private string unitsString; + + public ValueDisplayInfo(string formatString = "{0:0.00}", string unitsString = "mm") + { + this.formatString = formatString; + this.unitsString = unitsString; + } + + public void DisplaySizeInfo(Graphics2D graphics2D, Vector2 widthDisplayCenter, double size) + { + string displayString = formatString.FormatWith(size); + if (measureDisplayImage == null || measureDisplayedString != displayString) + { + measureDisplayedString = displayString; + TypeFacePrinter printer = new TypeFacePrinter(measureDisplayedString, 16); + TypeFacePrinter unitPrinter = new TypeFacePrinter(unitsString, 10); + Double unitPrinterOffset = 1; + + BorderDouble margin = new BorderDouble(5); + printer.Origin = new Vector2(margin.Left, margin.Bottom); + RectangleDouble bounds = printer.LocalBounds; + + unitPrinter.Origin = new Vector2(bounds.Right + unitPrinterOffset, margin.Bottom); + RectangleDouble unitPrinterBounds = unitPrinter.LocalBounds; + + measureDisplayImage = new ImageBuffer((int)(bounds.Width + margin.Width + unitPrinterBounds.Width + unitPrinterOffset), (int)(bounds.Height + margin.Height)); + // make sure the texture has mipmaps (so it can reduce well) + ImageGlPlugin glPlugin = ImageGlPlugin.GetImageGlPlugin(measureDisplayImage, true); + Graphics2D widthGraphics = measureDisplayImage.NewGraphics2D(); + widthGraphics.Clear(new RGBA_Bytes(RGBA_Bytes.White, 128)); + printer.Render(widthGraphics, RGBA_Bytes.Black); + unitPrinter.Render(widthGraphics, RGBA_Bytes.Black); + } + + widthDisplayCenter -= new Vector2(measureDisplayImage.Width / 2, measureDisplayImage.Height / 2); + graphics2D.Render(measureDisplayImage, widthDisplayCenter); + } + } +} \ No newline at end of file diff --git a/PartPreviewWindow/View3D/MeshViewerWidget.cs b/PartPreviewWindow/View3D/MeshViewerWidget.cs new file mode 100644 index 000000000..231529813 --- /dev/null +++ b/PartPreviewWindow/View3D/MeshViewerWidget.cs @@ -0,0 +1,888 @@ +/* +Copyright (c) 2014, Lars Brubaker +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.Agg; +using MatterHackers.Agg.Image; +using MatterHackers.Agg.OpenGlGui; +using MatterHackers.Agg.UI; +using MatterHackers.Agg.VertexSource; +using MatterHackers.DataConverters3D; +using MatterHackers.PolygonMesh; +using MatterHackers.PolygonMesh.Processors; +using MatterHackers.RayTracer; +using MatterHackers.RayTracer.Traceable; +using MatterHackers.RenderOpenGl; +using MatterHackers.RenderOpenGl.OpenGl; +using MatterHackers.VectorMath; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace MatterHackers.MeshVisualizer +{ + public enum BedShape { Rectangular, Circular }; + + public class DrawGlContentEventArgs : EventArgs + { + public bool ZBuffered { get; } + + public DrawGlContentEventArgs(bool zBuffered) + { + ZBuffered = zBuffered; + } + } + + public class MeshViewerWidget : GuiWidget + { + static public ImageBuffer BedImage = null; + public List interactionVolumes = new List(); + public InteractionVolume SelectedInteractionVolume { get; set; } = null; + public bool MouseDownOnInteractionVolume { get { return SelectedInteractionVolume != null; } } + + public PartProcessingInfo partProcessingInfo; + private static ImageBuffer lastCreatedBedImage = new ImageBuffer(); + + private static Dictionary materialColors = new Dictionary(); + private RGBA_Bytes bedBaseColor = new RGBA_Bytes(245, 245, 255); + static public Vector2 BedCenter { get; private set; } + private RGBA_Bytes bedMarkingsColor = RGBA_Bytes.Black; + private static BedShape bedShape = BedShape.Rectangular; + private static Mesh buildVolume = null; + private static Vector3 displayVolume; + private static Mesh printerBed = null; + private RenderTypes renderType = RenderTypes.Shaded; + + public double SnapGridDistance { get; set; } = 1; + + private TrackballTumbleWidget trackballTumbleWidget; + + private int volumeIndexWithMouseDown = -1; + + public MeshViewerWidget(Vector3 displayVolume, Vector2 bedCenter, BedShape bedShape, string startingTextMessage = "") + { + Scene.SelectionChanged += (sender, e) => + { + Invalidate(); + }; + RenderType = RenderTypes.Shaded; + RenderBed = true; + RenderBuildVolume = false; + //SetMaterialColor(1, RGBA_Bytes.LightGray, RGBA_Bytes.White); + BedColor = new RGBA_Floats(.8, .8, .8, .7).GetAsRGBA_Bytes(); + BuildVolumeColor = new RGBA_Floats(.2, .8, .3, .2).GetAsRGBA_Bytes(); + + trackballTumbleWidget = new TrackballTumbleWidget(this.World); + trackballTumbleWidget.DrawRotationHelperCircle = false; + trackballTumbleWidget.DrawGlContent += trackballTumbleWidget_DrawGlContent; + trackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Rotation; + + AddChild(trackballTumbleWidget); + + CreatePrintBed(displayVolume, bedCenter, bedShape); + + trackballTumbleWidget.AnchorAll(); + + partProcessingInfo = new PartProcessingInfo(startingTextMessage); + + GuiWidget labelContainer = new GuiWidget(); + labelContainer.AnchorAll(); + labelContainer.AddChild(partProcessingInfo); + labelContainer.Selectable = false; + + SetMaterialColor(1, ActiveTheme.Instance.PrimaryAccentColor); + + this.AddChild(labelContainer); + } + + public WorldView World { get; } = new WorldView(0, 0); + + public event EventHandler LoadDone; + + public bool AllowBedRenderingWhenEmpty { get; set; } + + public RGBA_Bytes BedColor { get; set; } + + public RGBA_Bytes BuildVolumeColor { get; set; } + + public Vector3 DisplayVolume { get { return displayVolume; } } + + public static AxisAlignedBoundingBox GetAxisAlignedBoundingBox(List meshGroups) + { + AxisAlignedBoundingBox totalMeshBounds = AxisAlignedBoundingBox.Empty; + bool first = true; + foreach (MeshGroup meshGroup in meshGroups) + { + AxisAlignedBoundingBox meshBounds = meshGroup.GetAxisAlignedBoundingBox(); + if (first) + { + totalMeshBounds = meshBounds; + first = false; + } + else + { + totalMeshBounds = AxisAlignedBoundingBox.Union(totalMeshBounds, meshBounds); + } + } + + return totalMeshBounds; + } + + public override void OnLoad(EventArgs args) + { + // some debug code to be able to click on parts + if (false) + { + AfterDraw += (sender, e) => + { + foreach (var child in Scene.Children) + { + this.World.RenderDebugAABB(e.graphics2D, child.TraceData().GetAxisAlignedBoundingBox()); + this.World.RenderDebugAABB(e.graphics2D, child.GetAxisAlignedBoundingBox(Matrix4X4.Identity)); + } + }; + } + + base.OnLoad(args); + } + + + public override void FindNamedChildrenRecursive(string nameToSearchFor, List foundChildren, RectangleDouble touchingBounds, SearchType seachType, bool allowInvalidItems = true) + { + foreach (var child in Scene.Children) + { + string object3DName = child.Name; + if (object3DName == null && child.MeshPath != null) + { + object3DName = Path.GetFileName(child.MeshPath); + } + + bool nameFound = false; + + if (seachType == SearchType.Exact) + { + if (object3DName == nameToSearchFor) + { + nameFound = true; + } + } + else + { + if (nameToSearchFor == "" + || object3DName.Contains(nameToSearchFor)) + { + nameFound = true; + } + } + + if (nameFound) + { + AxisAlignedBoundingBox bounds = child.TraceData().GetAxisAlignedBoundingBox(); + + RectangleDouble screenBoundsOfObject3D = RectangleDouble.ZeroIntersection; + for(int i=0; i<4; i++) + { + screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetTopCorner(i))); + screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetBottomCorner(i))); + } + + if (touchingBounds.IsTouching(screenBoundsOfObject3D)) + { + Vector3 renderPosition = bounds.Center; + Vector2 objectCenterScreenSpace = this.World.GetScreenPosition(renderPosition); + Point2D screenPositionOfObject3D = new Point2D((int)objectCenterScreenSpace.x, (int)objectCenterScreenSpace.y); + + foundChildren.Add(new WidgetAndPosition(this, screenPositionOfObject3D, object3DName)); + } + } + } + + base.FindNamedChildrenRecursive(nameToSearchFor, foundChildren, touchingBounds, seachType, allowInvalidItems); + } + + public InteractiveScene Scene { get; } = new InteractiveScene(); + + public Mesh PrinterBed { get { return printerBed; } } + + public bool RenderBed { get; set; } + + public bool RenderBuildVolume { get; set; } + + public RenderTypes RenderType + { + get { return renderType; } + set + { + if (renderType != value) + { + renderType = value; + foreach(var renderTransfrom in Scene.VisibleMeshes(Matrix4X4.Identity)) + { + renderTransfrom.MeshData.MarkAsChanged(); + } + } + } + } + + public TrackballTumbleWidget TrackballTumbleWidget + { + get + { + return trackballTumbleWidget; + } + } + + public static void AssertDebugNotDefined() + { +#if DEBUG + throw new Exception("DEBUG is defined and should not be!"); +#endif + } + + public static RGBA_Bytes GetMaterialColor(int materialIndexBase1) + { + lock (materialColors) + { + if (materialColors.ContainsKey(materialIndexBase1)) + { + return materialColors[materialIndexBase1]; + } + } + + // we currently expect at most 4 extruders + return RGBA_Floats.FromHSL((materialIndexBase1 % 4) / 4.0, .5, .5).GetAsRGBA_Bytes(); + } + + public static RGBA_Bytes GetSelectedMaterialColor(int materialIndexBase1) + { + double hue0To1; + double saturation0To1; + double lightness0To1; + GetMaterialColor(materialIndexBase1).GetAsRGBA_Floats().GetHSL(out hue0To1, out saturation0To1, out lightness0To1); + + // now make it a bit lighter and less saturated + saturation0To1 = Math.Min(1, saturation0To1 * 2); + lightness0To1 = Math.Min(1, lightness0To1 * 1.2); + + // we sort of expect at most 4 extruders + return RGBA_Floats.FromHSL(hue0To1, saturation0To1, lightness0To1).GetAsRGBA_Bytes(); + } + + public static void SetMaterialColor(int materialIndexBase1, RGBA_Bytes color) + { + lock (materialColors) + { + if (!materialColors.ContainsKey(materialIndexBase1)) + { + materialColors.Add(materialIndexBase1, color); + } + else + { + materialColors[materialIndexBase1] = color; + } + } + } + + public void CreateGlDataObject(IObject3D item) + { + if(item.Mesh != null) + { + GLMeshTrianglePlugin.Get(item.Mesh); + } + + foreach (IObject3D child in item.Children.Where(o => o.Mesh != null)) + { + GLMeshTrianglePlugin.Get(child.Mesh); + } + } + + public void CreatePrintBed(Vector3 displayVolume, Vector2 bedCenter, BedShape bedShape) + { + if (MeshViewerWidget.BedCenter == bedCenter + && MeshViewerWidget.bedShape == bedShape + && MeshViewerWidget.displayVolume == displayVolume) + { + return; + } + + MeshViewerWidget.BedCenter = bedCenter; + MeshViewerWidget.bedShape = bedShape; + MeshViewerWidget.displayVolume = displayVolume; + Vector3 displayVolumeToBuild = Vector3.ComponentMax(displayVolume, new Vector3(1, 1, 1)); + + double sizeForMarking = Math.Max(displayVolumeToBuild.x, displayVolumeToBuild.y); + double divisor = 10; + int skip = 1; + if (sizeForMarking > 1000) + { + divisor = 100; + skip = 10; + } + else if (sizeForMarking > 300) + { + divisor = 50; + skip = 5; + } + + switch (bedShape) + { + case BedShape.Rectangular: + if (displayVolumeToBuild.z > 0) + { + buildVolume = PlatonicSolids.CreateCube(displayVolumeToBuild); + foreach (Vertex vertex in buildVolume.Vertices) + { + vertex.Position = vertex.Position + new Vector3(0, 0, displayVolumeToBuild.z / 2); + } + } + CreateRectangularBedGridImage(displayVolumeToBuild, bedCenter, divisor, skip); + printerBed = PlatonicSolids.CreateCube(displayVolumeToBuild.x, displayVolumeToBuild.y, 1.8); + { + Face face = printerBed.Faces[0]; + MeshHelper.PlaceTextureOnFace(face, BedImage); + } + break; + + case BedShape.Circular: + { + if (displayVolumeToBuild.z > 0) + { + buildVolume = VertexSourceToMesh.Extrude(new Ellipse(new Vector2(), displayVolumeToBuild.x / 2, displayVolumeToBuild.y / 2), displayVolumeToBuild.z); + foreach (Vertex vertex in buildVolume.Vertices) + { + vertex.Position = vertex.Position + new Vector3(0, 0, .2); + } + } + CreateCircularBedGridImage((int)(displayVolumeToBuild.x / divisor), (int)(displayVolumeToBuild.y / divisor), skip); + printerBed = VertexSourceToMesh.Extrude(new Ellipse(new Vector2(), displayVolumeToBuild.x / 2, displayVolumeToBuild.y / 2), 2); + { + foreach (Face face in printerBed.Faces) + { + if (face.normal.z > 0) + { + FaceTextureData faceData = FaceTextureData.Get(face); + faceData.Textures.Add(BedImage); + foreach (FaceEdge faceEdge in face.FaceEdges()) + { + FaceEdgeTextureUvData edgeUV = FaceEdgeTextureUvData.Get(faceEdge); + edgeUV.TextureUV.Add(new Vector2((displayVolumeToBuild.x / 2 + faceEdge.firstVertex.Position.x) / displayVolumeToBuild.x, + (displayVolumeToBuild.y / 2 + faceEdge.firstVertex.Position.y) / displayVolumeToBuild.y)); + } + } + } + } + } + break; + + default: + throw new NotImplementedException(); + } + + foreach (Vertex vertex in printerBed.Vertices) + { + vertex.Position = vertex.Position - new Vector3(-bedCenter, 2.2); + } + + if (buildVolume != null) + { + foreach (Vertex vertex in buildVolume.Vertices) + { + vertex.Position = vertex.Position - new Vector3(-bedCenter, 2.2); + } + } + + Invalidate(); + } + + public bool SuppressUiVolumes { get; set; } = false; + + public async Task LoadItemIntoScene(string itemPath, Vector2 bedCenter = new Vector2(), string itemName = null) + { + if (File.Exists(itemPath)) + { + BeginProgressReporting("Loading Mesh"); + + // TODO: How to we handle mesh load errors? How do we report success? + IObject3D loadedItem = await Task.Run(() => Object3D.Load(itemPath, progress: ReportProgress0to100)); + if (loadedItem != null) + { + if (itemName != null) + { + loadedItem.Name = itemName; + } + + // SetMeshAfterLoad + Scene.ModifyChildren(children => + { + if (loadedItem.Mesh != null) + { + // STLs currently load directly into the mesh rather than as a group like AMF + children.Add(loadedItem); + } + else + { + children.AddRange(loadedItem.Children); + } + }); + + CreateGlDataObject(loadedItem); + } + else + { + partProcessingInfo.centeredInfoText.Text = string.Format("Sorry! No 3D view available\nfor this file."); + } + + EndProgressReporting(); + + // Invoke LoadDone event + LoadDone?.Invoke(this, null); + } + else + { + partProcessingInfo.centeredInfoText.Text = string.Format("{0}\n'{1}'", "File not found on disk.", Path.GetFileName(itemPath)); + } + } + + public override void OnDraw(Graphics2D graphics2D) + { + base.OnDraw(graphics2D); + + //if (!SuppressUiVolumes) + { + foreach (InteractionVolume interactionVolume in interactionVolumes) + { + interactionVolume.Draw2DContent(graphics2D); + } + } + } + + public override void OnMouseDown(MouseEventArgs mouseEvent) + { + base.OnMouseDown(mouseEvent); + + if (trackballTumbleWidget.MouseCaptured) + { + if (trackballTumbleWidget.TransformState == TrackBallController.MouseDownType.Rotation || mouseEvent.Button == MouseButtons.Right) + { + trackballTumbleWidget.DrawRotationHelperCircle = true; + } + } + + int volumeHitIndex; + Ray ray = this.World.GetRayForLocalBounds(mouseEvent.Position); + IntersectInfo info; + if (this.Scene.HasSelection + && !SuppressUiVolumes + && FindInteractionVolumeHit(ray, out volumeHitIndex, out info)) + { + MouseEvent3DArgs mouseEvent3D = new MouseEvent3DArgs(mouseEvent, ray, info); + volumeIndexWithMouseDown = volumeHitIndex; + interactionVolumes[volumeHitIndex].OnMouseDown(mouseEvent3D); + SelectedInteractionVolume = interactionVolumes[volumeHitIndex]; + } + else + { + SelectedInteractionVolume = null; + } + } + + public override void OnMouseMove(MouseEventArgs mouseEvent) + { + base.OnMouseMove(mouseEvent); + + if (SuppressUiVolumes) + { + return; + } + + Ray ray = this.World.GetRayForLocalBounds(mouseEvent.Position); + IntersectInfo info = null; + if (MouseDownOnInteractionVolume && volumeIndexWithMouseDown != -1) + { + MouseEvent3DArgs mouseEvent3D = new MouseEvent3DArgs(mouseEvent, ray, info); + interactionVolumes[volumeIndexWithMouseDown].OnMouseMove(mouseEvent3D); + } + else + { + MouseEvent3DArgs mouseEvent3D = new MouseEvent3DArgs(mouseEvent, ray, info); + + int volumeHitIndex; + FindInteractionVolumeHit(ray, out volumeHitIndex, out info); + + for (int i = 0; i < interactionVolumes.Count; i++) + { + if (i == volumeHitIndex) + { + interactionVolumes[i].MouseOver = true; + interactionVolumes[i].MouseMoveInfo = info; + } + else + { + interactionVolumes[i].MouseOver = false; + interactionVolumes[i].MouseMoveInfo = null; + } + + interactionVolumes[i].OnMouseMove(mouseEvent3D); + } + } + } + + public override void OnMouseUp(MouseEventArgs mouseEvent) + { + trackballTumbleWidget.DrawRotationHelperCircle = false; + Invalidate(); + + if(SuppressUiVolumes) + { + return; + } + + int volumeHitIndex; + Ray ray = this.World.GetRayForLocalBounds(mouseEvent.Position); + IntersectInfo info; + bool anyInteractionVolumeHit = FindInteractionVolumeHit(ray, out volumeHitIndex, out info); + MouseEvent3DArgs mouseEvent3D = new MouseEvent3DArgs(mouseEvent, ray, info); + + if (MouseDownOnInteractionVolume && volumeIndexWithMouseDown != -1) + { + interactionVolumes[volumeIndexWithMouseDown].OnMouseUp(mouseEvent3D); + SelectedInteractionVolume = null; + + volumeIndexWithMouseDown = -1; + } + else + { + volumeIndexWithMouseDown = -1; + + if (anyInteractionVolumeHit) + { + interactionVolumes[volumeHitIndex].OnMouseUp(mouseEvent3D); + } + SelectedInteractionVolume = null; + } + + base.OnMouseUp(mouseEvent); + } + + public void ResetView() + { + trackballTumbleWidget.ZeroVelocity(); + + this.World.Reset(); + this.World.Scale = .03; + this.World.Translate(-new Vector3(BedCenter)); + this.World.Rotate(Quaternion.FromEulerAngles(new Vector3(0, 0, MathHelper.Tau / 16))); + this.World.Rotate(Quaternion.FromEulerAngles(new Vector3(-MathHelper.Tau * .19, 0, 0))); + } + + private void CreateCircularBedGridImage(int linesInX, int linesInY, int increment = 1) + { + Vector2 bedImageCentimeters = new Vector2(linesInX, linesInY); + BedImage = new ImageBuffer(1024, 1024); + Graphics2D graphics2D = BedImage.NewGraphics2D(); + graphics2D.Clear(bedBaseColor); + { + double lineDist = BedImage.Width / (double)linesInX; + + int count = 1; + int pointSize = 16; + graphics2D.DrawString(count.ToString(), 4, 4, pointSize, color: bedMarkingsColor); + double currentRadius = lineDist; + Vector2 bedCenter = new Vector2(BedImage.Width / 2, BedImage.Height / 2); + for (double linePos = lineDist + BedImage.Width / 2; linePos < BedImage.Width; linePos += lineDist) + { + int linePosInt = (int)linePos; + graphics2D.DrawString((count * increment).ToString(), linePos + 2, BedImage.Height / 2, pointSize, color: bedMarkingsColor); + + Ellipse circle = new Ellipse(bedCenter, currentRadius); + Stroke outline = new Stroke(circle); + graphics2D.Render(outline, bedMarkingsColor); + currentRadius += lineDist; + count++; + } + + graphics2D.Line(0, BedImage.Height / 2, BedImage.Width, BedImage.Height / 2, bedMarkingsColor); + graphics2D.Line(BedImage.Width / 2, 0, BedImage.Width / 2, BedImage.Height, bedMarkingsColor); + } + } + + private void CreateRectangularBedGridImage(Vector3 displayVolumeToBuild, Vector2 bedCenter, double divisor, double skip) + { + lock (lastCreatedBedImage) + { + BedImage = new ImageBuffer(1024, 1024); + Graphics2D graphics2D = BedImage.NewGraphics2D(); + graphics2D.Clear(bedBaseColor); + { + double lineDist = BedImage.Width / (displayVolumeToBuild.x / divisor); + + double xPositionCm = (-(displayVolume.x / 2.0) + bedCenter.x) / divisor; + int xPositionCmInt = (int)Math.Round(xPositionCm); + double fraction = xPositionCm - xPositionCmInt; + int pointSize = 20; + graphics2D.DrawString((xPositionCmInt * skip).ToString(), 4, 4, pointSize, color: bedMarkingsColor); + for (double linePos = lineDist * (1 - fraction); linePos < BedImage.Width; linePos += lineDist) + { + xPositionCmInt++; + int linePosInt = (int)linePos; + int lineWidth = 1; + if (xPositionCmInt == 0) + { + lineWidth = 2; + } + graphics2D.Line(linePosInt, 0, linePosInt, BedImage.Height, bedMarkingsColor, lineWidth); + graphics2D.DrawString((xPositionCmInt * skip).ToString(), linePos + 4, 4, pointSize, color: bedMarkingsColor); + } + } + { + double lineDist = BedImage.Height / (displayVolumeToBuild.y / divisor); + + double yPositionCm = (-(displayVolume.y / 2.0) + bedCenter.y) / divisor; + int yPositionCmInt = (int)Math.Round(yPositionCm); + double fraction = yPositionCm - yPositionCmInt; + int pointSize = 20; + for (double linePos = lineDist * (1 - fraction); linePos < BedImage.Height; linePos += lineDist) + { + yPositionCmInt++; + int linePosInt = (int)linePos; + int lineWidth = 1; + if (yPositionCmInt == 0) + { + lineWidth = 2; + } + graphics2D.Line(0, linePosInt, BedImage.Height, linePosInt, bedMarkingsColor, lineWidth); + + graphics2D.DrawString((yPositionCmInt * skip).ToString(), 4, linePos + 4, pointSize, color: bedMarkingsColor); + } + } + + lastCreatedBedImage = BedImage; + } + } + + private bool FindInteractionVolumeHit(Ray ray, out int interactionVolumeHitIndex, out IntersectInfo info) + { + interactionVolumeHitIndex = -1; + if (interactionVolumes.Count == 0 || interactionVolumes[0].CollisionVolume == null) + { + info = null; + return false; + } + + List uiTraceables = new List(); + foreach (InteractionVolume interactionVolume in interactionVolumes) + { + if (interactionVolume.CollisionVolume != null) + { + IPrimitive traceData = interactionVolume.CollisionVolume; + uiTraceables.Add(new Transform(traceData, interactionVolume.TotalTransform)); + } + } + IPrimitive allUiObjects = BoundingVolumeHierarchy.CreateNewHierachy(uiTraceables); + + info = allUiObjects.GetClosestIntersection(ray); + if (info != null) + { + for (int i = 0; i < interactionVolumes.Count; i++) + { + List insideBounds = new List(); + if (interactionVolumes[i].CollisionVolume != null) + { + interactionVolumes[i].CollisionVolume.GetContained(insideBounds, info.closestHitObject.GetAxisAlignedBoundingBox()); + if (insideBounds.Contains(info.closestHitObject)) + { + interactionVolumeHitIndex = i; + return true; + } + } + } + } + + return false; + } + + private string progressReportingPrimaryTask = ""; + + public void BeginProgressReporting(string taskDescription) + { + progressReportingPrimaryTask = taskDescription; + + partProcessingInfo.Visible = true; + partProcessingInfo.progressControl.PercentComplete = 0; + partProcessingInfo.centeredInfoText.Text = taskDescription + "..."; + } + + public void EndProgressReporting() + { + progressReportingPrimaryTask = ""; + partProcessingInfo.Visible = false; + } + + public void ReportProgress0to100(double progress0To1, string processingState, out bool continueProcessing) + { + if (this.HasBeenClosed) + { + continueProcessing = false; + } + else + { + continueProcessing = true; + } + + UiThread.RunOnIdle(() => + { + int percentComplete = (int)(progress0To1 * 100); + partProcessingInfo.centeredInfoText.Text = "{0} {1}%...".FormatWith(progressReportingPrimaryTask, percentComplete); + partProcessingInfo.progressControl.PercentComplete = percentComplete; + + // Only assign to textbox if value passed through + if (processingState != null) + { + partProcessingInfo.centeredInfoDescription.Text = processingState; + } + }); + } + + private void DrawObject(IObject3D object3D, Matrix4X4 transform, bool parentSelected) + { + foreach(MeshAndTransform meshAndTransform in object3D.VisibleMeshes(transform)) + { + bool isSelected = parentSelected || + Scene.HasSelection && (object3D == Scene.SelectedItem || Scene.SelectedItem.Children.Contains(object3D)); + + MeshMaterialData meshData = MeshMaterialData.Get(meshAndTransform.MeshData); + RGBA_Bytes drawColor = object3D.Color; + if (drawColor.Alpha0To1 == 0) + { + drawColor = isSelected ? GetSelectedMaterialColor(meshData.MaterialIndex) : GetMaterialColor(meshData.MaterialIndex); + } + + GLHelper.Render(meshAndTransform.MeshData, drawColor, meshAndTransform.Matrix, RenderType); + } + } + + private void trackballTumbleWidget_DrawGlContent(object sender, EventArgs e) + { + foreach(var object3D in Scene.Children) + { + DrawObject(object3D, Matrix4X4.Identity, false); + } + + if (RenderBed) + { + GLHelper.Render(printerBed, this.BedColor); + } + + if (buildVolume != null && RenderBuildVolume) + { + GLHelper.Render(buildVolume, this.BuildVolumeColor); + } + + // we don't want to render the bed or build volume before we load a model. + if (Scene.HasChildren || AllowBedRenderingWhenEmpty) + { + if (false) // this is code to draw a small axis indicator + { + double big = 10; + double small = 1; + Mesh xAxis = PlatonicSolids.CreateCube(big, small, small); + GLHelper.Render(xAxis, RGBA_Bytes.Red); + Mesh yAxis = PlatonicSolids.CreateCube(small, big, small); + GLHelper.Render(yAxis, RGBA_Bytes.Green); + Mesh zAxis = PlatonicSolids.CreateCube(small, small, big); + GLHelper.Render(zAxis, RGBA_Bytes.Blue); + } + } + + DrawInteractionVolumes(e); + } + + private void DrawInteractionVolumes(EventArgs e) + { + if(SuppressUiVolumes) + { + return; + } + + // draw on top of anything that is already drawn + foreach (InteractionVolume interactionVolume in interactionVolumes) + { + if (interactionVolume.DrawOnTop) + { + GL.Disable(EnableCap.DepthTest); + interactionVolume.DrawGlContent(new DrawGlContentEventArgs(false)); + GL.Enable(EnableCap.DepthTest); + } + } + + // Draw again setting the depth buffer and ensuring that all the interaction objects are sorted as well as we can + foreach (InteractionVolume interactionVolume in interactionVolumes) + { + interactionVolume.DrawGlContent(new DrawGlContentEventArgs(true)); + } + } + + public class PartProcessingInfo : FlowLayoutWidget + { + internal TextWidget centeredInfoDescription; + internal TextWidget centeredInfoText; + internal ProgressControl progressControl; + + internal PartProcessingInfo(string startingTextMessage) + : base(FlowDirection.TopToBottom) + { + progressControl = new ProgressControl("", RGBA_Bytes.Black, RGBA_Bytes.Black); + progressControl.HAnchor = HAnchor.ParentCenter; + AddChild(progressControl); + progressControl.Visible = false; + progressControl.ProgressChanged += (sender, e) => + { + progressControl.Visible = true; + }; + + centeredInfoText = new TextWidget(startingTextMessage); + centeredInfoText.HAnchor = HAnchor.ParentCenter; + centeredInfoText.AutoExpandBoundsToText = true; + AddChild(centeredInfoText); + + centeredInfoDescription = new TextWidget(""); + centeredInfoDescription.HAnchor = HAnchor.ParentCenter; + centeredInfoDescription.AutoExpandBoundsToText = true; + AddChild(centeredInfoDescription); + + VAnchor |= VAnchor.ParentCenter; + HAnchor |= HAnchor.ParentCenter; + } + } + } +} \ No newline at end of file diff --git a/PartPreviewWindow/View3D/MouseEvent3DArgs.cs b/PartPreviewWindow/View3D/MouseEvent3DArgs.cs new file mode 100644 index 000000000..307e9caf1 --- /dev/null +++ b/PartPreviewWindow/View3D/MouseEvent3DArgs.cs @@ -0,0 +1,52 @@ +/* +Copyright (c) 2014, Lars Brubaker +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.Agg.UI; +using MatterHackers.RayTracer; +using MatterHackers.VectorMath; +using System; + +namespace MatterHackers.MeshVisualizer +{ + public class MouseEvent3DArgs : EventArgs + { + public IntersectInfo info; + public MouseEventArgs MouseEvent2D; + private Ray mouseRay; + + public Ray MouseRay { get { return mouseRay; } } + + public MouseEvent3DArgs(MouseEventArgs mouseEvent2D, Ray mouseRay, IntersectInfo info) + { + this.info = info; + this.MouseEvent2D = mouseEvent2D; + this.mouseRay = mouseRay; + } + } +} \ No newline at end of file diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 6cce66ec6..e6e04679d 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -28,7 +28,6 @@ either expressed or implied, of the FreeBSD Project. */ using System; -using System.ComponentModel; using System.IO; using MatterHackers.Agg; using MatterHackers.Agg.UI; @@ -434,7 +433,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow GetRenderType(), gcodeViewWidget.FeatureToStartOnRatio0To1, gcodeViewWidget.FeatureToEndOnRatio0To1, - new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }); + new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, + MeshViewerWidget.GetMaterialColor); gcodeViewWidget.gCodeRenderer.Render3D(renderInfo); } diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/ViewGcodeWidget.cs index f36900bb3..2e53ee268 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/ViewGcodeWidget.cs @@ -27,20 +27,20 @@ of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ +using System; +using System.Diagnostics; +using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.Transform; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.GCodeVisualizer; +using MatterHackers.Localizations; using MatterHackers.MatterControl.SlicerConfiguration; +using MatterHackers.MeshVisualizer; using MatterHackers.RenderOpenGl; using MatterHackers.RenderOpenGl.OpenGl; using MatterHackers.VectorMath; -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Threading.Tasks; -using MatterHackers.Localizations; namespace MatterHackers.MatterControl.PartPreviewWindow { @@ -299,7 +299,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow GCodeRenderInfo renderInfo = new GCodeRenderInfo(activeLayerIndex, activeLayerIndex, transform, layerScale, CreateRenderInfo(), FeatureToStartOnRatio0To1, FeatureToEndOnRatio0To1, - new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }); + new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, + MeshViewerWidget.GetMaterialColor); //using (new PerformanceTimer("GCode Timer", "Render")) { diff --git a/Tests/MatterControl.Tests/MatterControl.Tests.csproj b/Tests/MatterControl.Tests/MatterControl.Tests.csproj index d3d7306ac..ea11d77b9 100644 --- a/Tests/MatterControl.Tests/MatterControl.Tests.csproj +++ b/Tests/MatterControl.Tests/MatterControl.Tests.csproj @@ -83,6 +83,10 @@ {0B8D6F56-BD7F-4426-B858-D9292B084656} MatterControl + + {97d5ade3-c1b4-4b46-8a3e-718a4f7f079f} + MatterControl.Printing + {bb58ca42-991b-41b7-bde7-dcd2911df8b9} PrinterEmulator @@ -95,14 +99,6 @@ {04667764-dc7b-4b95-aef6-b4e6c87a54e9} DataConverters3D - - {F67AE800-B0C7-42A8-836F-597B4E74591C} - GCodeVisualizer - - - {a737bc76-165b-46c6-82b7-8871c7c92942} - MeshViewer - {E9102310-0029-4D8F-B1E9-88FBA6147D45} GuiAutomation diff --git a/TextCreator/TextCreator.csproj b/TextCreator/TextCreator.csproj index 1f0549b70..253db1bc5 100644 --- a/TextCreator/TextCreator.csproj +++ b/TextCreator/TextCreator.csproj @@ -166,10 +166,6 @@ {04667764-DC7B-4B95-AEF6-B4E6C87A54E9} DataConverters3D - - {A737BC76-165B-46C6-82B7-8871C7C92942} - MeshViewer - {74F6BB6C-9D02-4512-A59A-21940E35C532} Gui From baa9feba81690f1c62ea1c34b853dd0f3c7ac885 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 08:32:09 -0700 Subject: [PATCH 02/18] Closer to gcode/mesh viewer consolidation - Collapse right panel content into scene - Move GCode view options into overflow menu - Extract View3D options state from widgets to app model --- ApplicationView/ApplicationController.cs | 97 +++++ .../BaseClasses/PartPreview3DWidget.cs | 1 - PartPreviewWindow/PartPreviewContent.cs | 6 +- PartPreviewWindow/ViewGcodeBasic.cs | 354 +++++------------- PartPreviewWindow/ViewGcodeWidget.cs | 106 +----- 5 files changed, 211 insertions(+), 353 deletions(-) diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index bc53994e7..02facf458 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -79,6 +79,103 @@ namespace MatterHackers.MatterControl public ThemeConfig Theme { get; set; } = new ThemeConfig(); + public class View3DConfig + { + public bool RenderGrid + { + get + { + string value = UserSettings.Instance.get("GcodeViewerRenderGrid"); + if (value == null) + { + RenderGrid = true; + return true; + } + return (value == "True"); + } + set + { + UserSettings.Instance.set("GcodeViewerRenderGrid", value.ToString()); + } + } + + public bool RenderMoves + { + get { return (UserSettings.Instance.get("GcodeViewerRenderMoves") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderMoves", value.ToString()); + } + } + + public bool RenderRetractions + { + get { return (UserSettings.Instance.get("GcodeViewerRenderRetractions") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderRetractions", value.ToString()); + } + } + + public bool RenderSpeeds + { + get { return (UserSettings.Instance.get("GcodeViewerRenderSpeeds") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderSpeeds", value.ToString()); + } + } + + public bool SimulateExtrusion + { + get { return (UserSettings.Instance.get("GcodeViewerSimulateExtrusion") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerSimulateExtrusion", value.ToString()); + } + } + + public bool TransparentExtrusion + { + get { return (UserSettings.Instance.get("GcodeViewerTransparentExtrusion") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerTransparentExtrusion", value.ToString()); + } + } + + public bool HideExtruderOffsets + { + get + { + string value = UserSettings.Instance.get("GcodeViewerHideExtruderOffsets"); + if (value == null) + { + return true; + } + return (value == "True"); + } + set + { + UserSettings.Instance.set("GcodeViewerHideExtruderOffsets", value.ToString()); + } + } + + public bool SyncToPrint + { + get => UserSettings.Instance.get("GcodeViewerHideExtruderOffsets") == "True"; + set => UserSettings.Instance.set("LayerViewSyncToPrint", value.ToString()); + } + } + + public class ApplicationConfig + { + public View3DConfig View3D { get; } = new View3DConfig(); + } + + + public ApplicationConfig Options { get; } = new ApplicationConfig(); + public Action RedeemDesignCode; public Action EnterShareCode; diff --git a/PartPreviewWindow/BaseClasses/PartPreview3DWidget.cs b/PartPreviewWindow/BaseClasses/PartPreview3DWidget.cs index fc223e875..d393dd516 100644 --- a/PartPreviewWindow/BaseClasses/PartPreview3DWidget.cs +++ b/PartPreviewWindow/BaseClasses/PartPreview3DWidget.cs @@ -50,7 +50,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow protected bool allowAutoRotate = false; protected GuiWidget buttonRightPanelDisabledCover; - protected FlowLayoutWidget buttonRightPanel; public MeshViewerWidget meshViewerWidget; diff --git a/PartPreviewWindow/PartPreviewContent.cs b/PartPreviewWindow/PartPreviewContent.cs index c60908ed2..4f319f368 100644 --- a/PartPreviewWindow/PartPreviewContent.cs +++ b/PartPreviewWindow/PartPreviewContent.cs @@ -138,8 +138,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { return modelViewer.ShowOverflowMenu(); } - - return null; + else + { + return gcodeViewer.ShowOverflowMenu(); + } }; // The 3D model view diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index e6e04679d..579d8578a 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -59,15 +59,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private FlowLayoutWidget buttonBottomPanel; private FlowLayoutWidget layerSelectionButtonsPanel; - private FlowLayoutWidget modelOptionsContainer; - private FlowLayoutWidget displayOptionsContainer; private ViewControlsToggle viewControlsToggle; - private CheckBox expandModelOptions; - private CheckBox expandDisplayOptions; - private CheckBox syncToPrint; - private CheckBox showSpeeds; - private GuiWidget gcodeDisplayWidget; private ColorGradientWidget gradientWidget; @@ -95,9 +88,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private TextImageButtonFactory textImageButtonFactory; private TextImageButtonFactory ExpandMenuOptionFactory; + private ApplicationController.View3DConfig options; + public ViewGcodeBasic(Vector3 viewerVolume, Vector2 bedCenter, BedShape bedShape, WindowMode windowMode, ViewControls3D viewControls3D, ThemeConfig theme) : base(viewControls3D) { + this.options = ApplicationController.Instance.Options.View3D; this.textImageButtonFactory = theme.textImageButtonFactory; this.ExpandMenuOptionFactory = theme.ExpandMenuOptionFactory; @@ -239,9 +235,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; } - FlowLayoutWidget centerPartPreviewAndControls = new FlowLayoutWidget(FlowDirection.LeftToRight); - centerPartPreviewAndControls.AnchorAll(); - gcodeDisplayWidget = new GuiWidget() { HAnchor = HAnchor.ParentLeftRight, @@ -293,11 +286,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } SetProcessingMessage(firstProcessingMessage); - centerPartPreviewAndControls.AddChild(gcodeDisplayWidget); - buttonRightPanel = CreateRightButtonPanel(); - buttonRightPanel.Visible = false; - centerPartPreviewAndControls.AddChild(buttonRightPanel); + mainContainerTopToBottom.AddChild(gcodeDisplayWidget); // add in a spacer layerSelectionButtonsPanel.AddChild(new GuiWidget() @@ -306,7 +296,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); buttonBottomPanel.AddChild(layerSelectionButtonsPanel); - mainContainerTopToBottom.AddChild(centerPartPreviewAndControls); mainContainerTopToBottom.AddChild(buttonBottomPanel); this.AddChild(mainContainerTopToBottom); @@ -372,9 +361,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } }; - - expandModelOptions.CheckedStateChanged += expandModelOptions_CheckedStateChanged; - expandDisplayOptions.CheckedStateChanged += expandDisplayOptions_CheckedStateChanged; } private void MeshViewerWidget_Closed(object sender, ClosedEventArgs e) @@ -388,28 +374,30 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private RenderType GetRenderType() { + var options = ApplicationController.Instance.Options.View3D; + RenderType renderType = RenderType.Extrusions; - if (gcodeViewWidget.RenderMoves) + if (options.RenderMoves) { renderType |= RenderType.Moves; } - if (gcodeViewWidget.RenderRetractions) + if (options.RenderRetractions) { renderType |= RenderType.Retractions; } - if (gcodeViewWidget.RenderSpeeds) + if (options.RenderSpeeds) { renderType |= RenderType.SpeedColors; } - if (gcodeViewWidget.SimulateExtrusion) + if (options.SimulateExtrusion) { renderType |= RenderType.SimulateExtrusion; } - if (gcodeViewWidget.TransparentExtrusion) + if (options.TransparentExtrusion) { renderType |= RenderType.TransparentExtrusion; } - if (gcodeViewWidget.HideExtruderOffsets) + if (options.HideExtruderOffsets) { renderType |= RenderType.HideExtruderOffsets; } @@ -456,72 +444,23 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - private FlowLayoutWidget CreateRightButtonPanel() + private FlowLayoutWidget CreateModelInfo() { - FlowLayoutWidget buttonRightPanel = new FlowLayoutWidget(FlowDirection.TopToBottom); - buttonRightPanel.Width = 200; - { - string label = "Model".Localize().ToUpper(); - expandModelOptions = ExpandMenuOptionFactory.GenerateCheckBoxButton(label, - View3DWidget.ArrowRight, - View3DWidget.ArrowDown); - expandModelOptions.Margin = new BorderDouble(bottom: 2); - buttonRightPanel.AddChild(expandModelOptions); - expandModelOptions.Checked = true; - - modelOptionsContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); - modelOptionsContainer.HAnchor = HAnchor.ParentLeftRight; - //modelOptionsContainer.Visible = false; - buttonRightPanel.AddChild(modelOptionsContainer); - - expandDisplayOptions = ExpandMenuOptionFactory.GenerateCheckBoxButton("Display".Localize().ToUpper(), - View3DWidget.ArrowRight, - View3DWidget.ArrowDown); - expandDisplayOptions.Name = "Display Checkbox"; - expandDisplayOptions.Margin = new BorderDouble(bottom: 2); - buttonRightPanel.AddChild(expandDisplayOptions); - expandDisplayOptions.Checked = false; - - displayOptionsContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); - displayOptionsContainer.HAnchor = HAnchor.ParentLeftRight; - displayOptionsContainer.Padding = new BorderDouble(left: 6); - displayOptionsContainer.Visible = false; - buttonRightPanel.AddChild(displayOptionsContainer); - - GuiWidget verticalSpacer = new GuiWidget(); - verticalSpacer.VAnchor = VAnchor.ParentBottomTop; - buttonRightPanel.AddChild(verticalSpacer); - } - - buttonRightPanel.Padding = new BorderDouble(6, 6); - buttonRightPanel.Margin = new BorderDouble(0, 1); - buttonRightPanel.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; - buttonRightPanel.VAnchor = VAnchor.ParentBottomTop; - - return buttonRightPanel; - } - - private void CreateOptionsContent() - { - AddModelInfo(modelOptionsContainer); - AddDisplayControls(displayOptionsContainer); - } - - private void AddModelInfo(FlowLayoutWidget buttonPanel) - { - buttonPanel.CloseAllChildren(); - double oldWidth = textImageButtonFactory.FixedWidth; textImageButtonFactory.FixedWidth = 44 * GuiWidget.DeviceScale; - FlowLayoutWidget modelInfoContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); - modelInfoContainer.HAnchor = HAnchor.ParentLeftRight; - modelInfoContainer.Padding = new BorderDouble(5); + var modelInfoContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + Padding = new BorderDouble(5), + Margin = new BorderDouble(0, 0, 35, 5), + BackgroundColor = new RGBA_Bytes(0, 0, 0, ViewControlsBase.overlayAlpha), + HAnchor = HAnchor.ParentRight | HAnchor.AbsolutePosition, + VAnchor = VAnchor.ParentTop | VAnchor.FitToChildren, + Width = 150 + }; - string printTimeLabel = "Print Time".Localize(); - string printTimeLabelFull = string.Format("{0}:", printTimeLabel); // put in the print time - modelInfoContainer.AddChild(new TextWidget(printTimeLabelFull, textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); + modelInfoContainer.AddChild(new TextWidget("Print Time".Localize() + ":", textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); { string timeRemainingText = "---"; @@ -533,43 +472,34 @@ namespace MatterHackers.MatterControl.PartPreviewWindow secondsRemaining = secondsRemaining % 60; if (hoursRemaining > 0) { - timeRemainingText = string.Format("{0} h, {1} min", hoursRemaining, minutesRemaining); + timeRemainingText = $"{hoursRemaining} h, {minutesRemaining} min"; } else { - timeRemainingText = string.Format("{0} min", minutesRemaining); + timeRemainingText = $"{minutesRemaining} min"; } } - GuiWidget estimatedPrintTime = new TextWidget(string.Format("{0}", timeRemainingText), textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 14); - //estimatedPrintTime.HAnchor = Agg.UI.HAnchor.ParentLeft; + GuiWidget estimatedPrintTime = new TextWidget(timeRemainingText, textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 14); estimatedPrintTime.Margin = new BorderDouble(0, 9, 0, 3); modelInfoContainer.AddChild(estimatedPrintTime); } - //modelInfoContainer.AddChild(new TextWidget("Size:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - - string filamentLengthLabel = "Filament Length".Localize(); - string filamentLengthLabelFull = string.Format("{0}:", filamentLengthLabel); // show the filament used - modelInfoContainer.AddChild(new TextWidget(filamentLengthLabelFull, textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); + modelInfoContainer.AddChild(new TextWidget("Filament Length".Localize() + ":", textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); { double filamentUsed = gcodeViewWidget.LoadedGCode.GetFilamentUsedMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); GuiWidget estimatedPrintTime = new TextWidget(string.Format("{0:0.0} mm", filamentUsed), pointSize: 14, textColor: ActiveTheme.Instance.PrimaryTextColor); - //estimatedPrintTime.HAnchor = Agg.UI.HAnchor.ParentLeft; estimatedPrintTime.Margin = new BorderDouble(0, 9, 0, 3); modelInfoContainer.AddChild(estimatedPrintTime); } - string filamentVolumeLabel = "Filament Volume".Localize(); - string filamentVolumeLabelFull = string.Format("{0}:", filamentVolumeLabel); - modelInfoContainer.AddChild(new TextWidget(filamentVolumeLabelFull, textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); + modelInfoContainer.AddChild(new TextWidget("Filament Volume".Localize() + ":", textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); { double filamentMm3 = gcodeViewWidget.LoadedGCode.GetFilamentCubicMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); GuiWidget estimatedPrintTime = new TextWidget(string.Format("{0:0.00} cm³", filamentMm3 / 1000), pointSize: 14, textColor: ActiveTheme.Instance.PrimaryTextColor); - //estimatedPrintTime.HAnchor = Agg.UI.HAnchor.ParentLeft; estimatedPrintTime.Margin = new BorderDouble(0, 9, 0, 3); modelInfoContainer.AddChild(estimatedPrintTime); } @@ -577,11 +507,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow modelInfoContainer.AddChild(GetEstimatedMassInfo()); modelInfoContainer.AddChild(GetEstimatedCostInfo()); + // TODO: Every time you click Generate we wire up a listener - only when we close do they get released. This is a terrible pattern that has a good chance of creating a high leak scenario. Since RootedEventHandlers are normally only cleared when a widget is closed, we should **only** register them in widget constructors PrinterConnection.Instance.CommunicationStateChanged.RegisterEvent(HookUpGCodeMessagesWhenDonePrinting, ref unregisterEvents); - buttonPanel.AddChild(modelInfoContainer); - textImageButtonFactory.FixedWidth = oldWidth; + + return modelInfoContainer; } double totalMass @@ -668,137 +599,92 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return estimatedCostInfo; } - private void AddLayerInfo(FlowLayoutWidget buttonPanel) + internal GuiWidget ShowOverflowMenu() { double oldWidth = textImageButtonFactory.FixedWidth; textImageButtonFactory.FixedWidth = 44 * GuiWidget.DeviceScale; - FlowLayoutWidget layerInfoContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); - layerInfoContainer.HAnchor = HAnchor.ParentLeftRight; - layerInfoContainer.Padding = new BorderDouble(5); - - layerInfoContainer.AddChild(new TextWidget("Layer Number:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Layer Height:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Num GCodes:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Filament Used:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Weight:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Print Time:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Extrude Speeds:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Move Speeds:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - layerInfoContainer.AddChild(new TextWidget("Retract Speeds:", textColor: ActiveTheme.Instance.PrimaryTextColor)); - - buttonPanel.AddChild(layerInfoContainer); - - textImageButtonFactory.FixedWidth = oldWidth; - } - - private void AddDisplayControls(FlowLayoutWidget buttonPanel) - { - buttonPanel.CloseAllChildren(); - - double oldWidth = textImageButtonFactory.FixedWidth; - textImageButtonFactory.FixedWidth = 44 * GuiWidget.DeviceScale; - - FlowLayoutWidget layerInfoContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); - layerInfoContainer.HAnchor = HAnchor.ParentLeftRight; - layerInfoContainer.Padding = new BorderDouble(5); + var popupContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + HAnchor = HAnchor.ParentLeftRight, + Padding = 12, + BackgroundColor = RGBA_Bytes.White + }; // put in a show grid check box + CheckBox showGrid = new CheckBox("Print Bed".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + showGrid.Checked = options.RenderGrid; + meshViewerWidget.RenderBed = showGrid.Checked; + showGrid.CheckedStateChanged += (sender, e) => { - CheckBox showGrid = new CheckBox("Print Bed".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); - showGrid.Checked = gcodeViewWidget.RenderGrid; - meshViewerWidget.RenderBed = showGrid.Checked; - showGrid.CheckedStateChanged += (sender, e) => - { - gcodeViewWidget.RenderGrid = showGrid.Checked; - meshViewerWidget.RenderBed = showGrid.Checked; - }; - layerInfoContainer.AddChild(showGrid); - } + options.RenderGrid = showGrid.Checked; + }; + popupContainer.AddChild(showGrid); // put in a show moves checkbox + var showMoves = new CheckBox("Moves".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + showMoves.Checked = options.RenderMoves; + showMoves.CheckedStateChanged += (sender, e) => { - CheckBox showMoves = new CheckBox("Moves".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); - showMoves.Checked = gcodeViewWidget.RenderMoves; - showMoves.CheckedStateChanged += (sender, e) => - { - gcodeViewWidget.RenderMoves = showMoves.Checked; - }; - layerInfoContainer.AddChild(showMoves); - } + options.RenderMoves = showMoves.Checked; + }; + popupContainer.AddChild(showMoves); // put in a show Retractions checkbox + CheckBox showRetractions = new CheckBox("Retractions".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + showRetractions.Checked = options.RenderRetractions; + showRetractions.CheckedStateChanged += (sender, e) => { - CheckBox showRetractions = new CheckBox("Retractions".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); - showRetractions.Checked = gcodeViewWidget.RenderRetractions; - showRetractions.CheckedStateChanged += (sender, e) => - { - gcodeViewWidget.RenderRetractions = showRetractions.Checked; - }; - layerInfoContainer.AddChild(showRetractions); - } + options.RenderRetractions = showRetractions.Checked; + }; + popupContainer.AddChild(showRetractions); + // put in a show speed checkbox + var showSpeeds = new CheckBox("Speeds".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + showSpeeds.Checked = options.RenderSpeeds; + //showSpeeds.Checked = gradient.Visible; + showSpeeds.CheckedStateChanged += (sender, e) => { - showSpeeds = new CheckBox("Speeds".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); - showSpeeds.Checked = gcodeViewWidget.RenderSpeeds; - //showSpeeds.Checked = gradient.Visible; - showSpeeds.CheckedStateChanged += (sender, e) => - { - /* if (!showSpeeds.Checked) - { - gradient.Visible = false; - } - else - { - gradient.Visible = true; - }*/ + gradientWidget.Visible = showSpeeds.Checked; + options.RenderSpeeds = showSpeeds.Checked; + }; - gradientWidget.Visible = showSpeeds.Checked; - - gcodeViewWidget.RenderSpeeds = showSpeeds.Checked; - }; - - layerInfoContainer.AddChild(showSpeeds); - } + popupContainer.AddChild(showSpeeds); // put in a simulate extrusion checkbox + var simulateExtrusion = new CheckBox("Extrusion".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + simulateExtrusion.Checked = options.SimulateExtrusion; + simulateExtrusion.CheckedStateChanged += (sender, e) => { - CheckBox simulateExtrusion = new CheckBox("Extrusion".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); - simulateExtrusion.Checked = gcodeViewWidget.SimulateExtrusion; - simulateExtrusion.CheckedStateChanged += (sender, e) => - { - gcodeViewWidget.SimulateExtrusion = simulateExtrusion.Checked; - }; - layerInfoContainer.AddChild(simulateExtrusion); - } + options.SimulateExtrusion = simulateExtrusion.Checked; + }; + popupContainer.AddChild(simulateExtrusion); // put in a render extrusion transparent checkbox + var transparentExtrusion = new CheckBox("Transparent".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor) { - CheckBox transparentExtrusion = new CheckBox("Transparent".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor) - { - Checked = gcodeViewWidget.TransparentExtrusion, - Margin = new BorderDouble(5, 0, 0, 0), - HAnchor = HAnchor.ParentLeft, - }; + Checked = options.TransparentExtrusion, + Margin = new BorderDouble(5, 0, 0, 0), + HAnchor = HAnchor.ParentLeft, + }; - transparentExtrusion.CheckedStateChanged += (sender, e) => - { - gcodeViewWidget.TransparentExtrusion = transparentExtrusion.Checked; - }; - layerInfoContainer.AddChild(transparentExtrusion); - } + transparentExtrusion.CheckedStateChanged += (sender, e) => + { + options.TransparentExtrusion = transparentExtrusion.Checked; + }; + popupContainer.AddChild(transparentExtrusion); // put in a simulate extrusion checkbox if (ActiveSliceSettings.Instance.GetValue(SettingsKey.extruder_count) > 1) { CheckBox hideExtruderOffsets = new CheckBox("Hide Offsets", textColor: ActiveTheme.Instance.PrimaryTextColor); - hideExtruderOffsets.Checked = gcodeViewWidget.HideExtruderOffsets; + hideExtruderOffsets.Checked = options.HideExtruderOffsets; hideExtruderOffsets.CheckedStateChanged += (sender, e) => { - gcodeViewWidget.HideExtruderOffsets = hideExtruderOffsets.Checked; + options.HideExtruderOffsets = hideExtruderOffsets.Checked; }; - layerInfoContainer.AddChild(hideExtruderOffsets); + popupContainer.AddChild(hideExtruderOffsets); } // Respond to user driven view mode change events and store and switch to the new mode @@ -814,15 +700,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Put in the sync to print checkbox if (windowMode == WindowMode.Embeded) { - syncToPrint = new CheckBox("Sync To Print".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + var syncToPrint = new CheckBox("Sync To Print".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); syncToPrint.Checked = (UserSettings.Instance.get("LayerViewSyncToPrint") == "True"); syncToPrint.Name = "Sync To Print Checkbox"; - syncToPrint.CheckedStateChanged += (sender, e) => + syncToPrint.CheckedStateChanged += (s, e) => { - UserSettings.Instance.set("LayerViewSyncToPrint", syncToPrint.Checked.ToString()); + options.SyncToPrint = syncToPrint.Checked; + SetSyncToPrintVisibility(); }; - layerInfoContainer.AddChild(syncToPrint); + popupContainer.AddChild(syncToPrint); // The idea here is we just got asked to rebuild the window (and it is being created now) // because the gcode finished creating for the print that is printing. @@ -835,17 +722,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow generateGCodeButton.Visible = false; + // TODO: Bad pattern - figure out how to revise // However if the print finished or is canceled we are going to want to get updates again. So, hook the status event PrinterConnection.Instance.CommunicationStateChanged.RegisterEvent(HookUpGCodeMessagesWhenDonePrinting, ref unregisterEvents); UiThread.RunOnIdle(SetSyncToPrintVisibility); } } - //layerInfoContainer.AddChild(new CheckBox("Show Retractions", textColor: ActiveTheme.Instance.PrimaryTextColor)); - - buttonPanel.AddChild(layerInfoContainer); - textImageButtonFactory.FixedWidth = oldWidth; + + return popupContainer; } private void SetSyncToPrintVisibility() @@ -854,7 +740,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { bool printerIsRunningPrint = PrinterConnection.Instance.PrinterIsPaused || PrinterConnection.Instance.PrinterIsPrinting; - if (syncToPrint.Checked && printerIsRunningPrint) + if (options.SyncToPrint && printerIsRunningPrint) { SetAnimationPosition(); //navigationWidget.Visible = false; @@ -934,8 +820,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnDraw(Graphics2D graphics2D) { bool printerIsRunningPrint = PrinterConnection.Instance.PrinterIsPaused || PrinterConnection.Instance.PrinterIsPrinting; - if (syncToPrint != null - && syncToPrint.Checked + if (options.SyncToPrint && printerIsRunningPrint) { SetAnimationPosition(); @@ -1076,9 +961,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow AddChild(gradientWidget); gradientWidget.Visible = false; - CreateOptionsContent(); - setGradientVisibility(); - buttonRightPanel.Visible = true; + gradientWidget.Visible = options.RenderSpeeds; + viewControlsToggle.Visible = true; setLayerWidget?.Close(); @@ -1113,19 +997,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow BoundsChanged += new EventHandler(PartPreviewGCode_BoundsChanged); - meshViewerWidget.partProcessingInfo.Visible = false; - } - } + this.AddChild(CreateModelInfo()); - private void setGradientVisibility() - { - if (showSpeeds.Checked) - { - gradientWidget.Visible = true; - } - else - { - gradientWidget.Visible = false; + meshViewerWidget.partProcessingInfo.Visible = false; } } @@ -1170,36 +1044,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow layerRenderRatioSlider.TotalWidthInPixels = gcodeDisplayWidget.Width - 100; } - private void AddHandlers() - { - expandModelOptions.CheckedStateChanged += expandModelOptions_CheckedStateChanged; - expandDisplayOptions.CheckedStateChanged += expandDisplayOptions_CheckedStateChanged; - } - - private void expandModelOptions_CheckedStateChanged(object sender, EventArgs e) - { - if (modelOptionsContainer.Visible = expandModelOptions.Checked) - { - if (expandModelOptions.Checked == true) - { - expandDisplayOptions.Checked = false; - } - modelOptionsContainer.Visible = expandModelOptions.Checked; - } - } - - private void expandDisplayOptions_CheckedStateChanged(object sender, EventArgs e) - { - if (displayOptionsContainer.Visible != expandDisplayOptions.Checked) - { - if (expandDisplayOptions.Checked == true) - { - expandModelOptions.Checked = false; - } - displayOptionsContainer.Visible = expandDisplayOptions.Checked; - } - } - public override void OnClosed(ClosedEventArgs e) { UnHookWidgetThatHasKeyDownHooked(); diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/ViewGcodeWidget.cs index 2e53ee268..3aa6b820e 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/ViewGcodeWidget.cs @@ -47,26 +47,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public class ViewGcodeWidget : GuiWidget { public event EventHandler DoneLoading; - - public bool RenderGrid - { - get - { - string value = UserSettings.Instance.get("GcodeViewerRenderGrid"); - if (value == null) - { - RenderGrid = true; - return true; - } - return (value == "True"); - } - set - { - UserSettings.Instance.set("GcodeViewerRenderGrid", value.ToString()); - Invalidate(); - } - } - + public double FeatureToStartOnRatio0To1 = 0; public double FeatureToEndOnRatio0To1 = 1; @@ -74,74 +55,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public ETransformState TransformState { get; set; } - public bool RenderMoves - { - get { return (UserSettings.Instance.get("GcodeViewerRenderMoves") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderMoves", value.ToString()); - Invalidate(); - } - } - - public bool RenderRetractions - { - get { return (UserSettings.Instance.get("GcodeViewerRenderRetractions") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderRetractions", value.ToString()); - Invalidate(); - } - } - - public bool RenderSpeeds - { - get { return (UserSettings.Instance.get("GcodeViewerRenderSpeeds") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderSpeeds", value.ToString()); - Invalidate(); - } - } - - public bool SimulateExtrusion - { - get { return (UserSettings.Instance.get("GcodeViewerSimulateExtrusion") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerSimulateExtrusion", value.ToString()); - Invalidate(); - } - } - - public bool TransparentExtrusion - { - get { return (UserSettings.Instance.get("GcodeViewerTransparentExtrusion") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerTransparentExtrusion", value.ToString()); - Invalidate(); - } - } - - public bool HideExtruderOffsets - { - get - { - string value = UserSettings.Instance.get("GcodeViewerHideExtruderOffsets"); - if (value == null) - { - return true; - } - return (value == "True"); - } - set - { - UserSettings.Instance.set("GcodeViewerHideExtruderOffsets", value.ToString()); - Invalidate(); - } - } - private Vector2 lastMousePosition = new Vector2(0, 0); private Vector2 mouseDownPosition = new Vector2(0, 0); @@ -211,8 +124,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private ReportProgressRatio progressReporter; + private ApplicationController.View3DConfig options; public ViewGcodeWidget(Vector2 gridSizeMm, Vector2 gridCenterMm, ReportProgressRatio progressReporter) { + this.options = ApplicationController.Instance.Options.View3D; this.progressReporter = progressReporter; this.gridSizeMm = gridSizeMm; this.gridCenterMm = gridCenterMm; @@ -276,7 +191,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { Affine transform = TotalTransform; - if (RenderGrid) + if (options.RenderGrid) { //using (new PerformanceTimer("GCode Timer", "Render Grid")) { @@ -314,28 +229,29 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private RenderType CreateRenderInfo() { + var options = ApplicationController.Instance.Options.View3D; RenderType renderType = RenderType.Extrusions; - if (RenderMoves) + if (options.RenderMoves) { renderType |= RenderType.Moves; } - if (RenderRetractions) + if (options.RenderRetractions) { renderType |= RenderType.Retractions; } - if (RenderSpeeds) + if (options.RenderSpeeds) { renderType |= RenderType.SpeedColors; } - if (SimulateExtrusion) + if (options.SimulateExtrusion) { renderType |= RenderType.SimulateExtrusion; } - if (TransparentExtrusion) + if (options.TransparentExtrusion) { renderType |= RenderType.TransparentExtrusion; } - if (HideExtruderOffsets) + if (options.HideExtruderOffsets) { renderType |= RenderType.HideExtruderOffsets; } From 6300e4468f656405c8876ba687471e8e984496e8 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 08:53:19 -0700 Subject: [PATCH 03/18] Extract button factory to model, use lambda events & initializer pattern --- ApplicationView/ThemeConfig.cs | 9 +++++ PartPreviewWindow/ViewGcodeBasic.cs | 53 +++++++++++++---------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/ApplicationView/ThemeConfig.cs b/ApplicationView/ThemeConfig.cs index 2b461cfa1..840f4182a 100644 --- a/ApplicationView/ThemeConfig.cs +++ b/ApplicationView/ThemeConfig.cs @@ -82,6 +82,7 @@ namespace MatterHackers.MatterControl public int SplitterWidth => (int)(7 * (GuiWidget.DeviceScale <= 1 ? GuiWidget.DeviceScale : GuiWidget.DeviceScale * 1.4)); public RGBA_Bytes SlightShade { get; } = new RGBA_Bytes(0, 0, 0, 40); + public TextImageButtonFactory GCodeLayerButtons { get; internal set; } private EventHandler unregisterEvents; @@ -219,6 +220,14 @@ namespace MatterHackers.MatterControl checkedBorderColor = RGBA_Bytes.White }; + this.GCodeLayerButtons = new TextImageButtonFactory() + { + normalTextColor = ActiveTheme.Instance.PrimaryTextColor, + hoverTextColor = ActiveTheme.Instance.PrimaryTextColor, + disabledTextColor = ActiveTheme.Instance.PrimaryTextColor, + pressedTextColor = ActiveTheme.Instance.PrimaryTextColor + }; + #region PartPreviewWidget if (UserSettings.Instance.IsTouchScreen) { diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 579d8578a..fd54fefe2 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -970,8 +970,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow setLayerWidget.VAnchor = Agg.UI.VAnchor.ParentTop; layerSelectionButtonsPanel.AddChild(setLayerWidget); + navigationWidget?.Close(); - navigationWidget = new LayerNavigationWidget(gcodeViewWidget); + navigationWidget = new LayerNavigationWidget(gcodeViewWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); navigationWidget.Margin = new BorderDouble(0, 0, 20, 0); layerSelectionButtonsPanel.AddChild(navigationWidget); @@ -1139,54 +1140,46 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public class LayerNavigationWidget : FlowLayoutWidget { - private Button prevLayerButton; - private Button nextLayerButton; private TextWidget layerCountTextWidget; private ViewGcodeWidget gcodeViewWidget; - private TextImageButtonFactory textImageButtonFactory = new TextImageButtonFactory(); - - public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget) + + public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) : base(FlowDirection.LeftToRight) { this.gcodeViewWidget = gcodeViewWidget; - textImageButtonFactory.normalTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.hoverTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.disabledTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.pressedTextColor = ActiveTheme.Instance.PrimaryTextColor; - - prevLayerButton = textImageButtonFactory.Generate("<<"); - prevLayerButton.Click += prevLayer_ButtonClick; + var prevLayerButton = buttonFactory.Generate("<<"); + prevLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); + }; this.AddChild(prevLayerButton); - layerCountTextWidget = new TextWidget("/1____", 12); - layerCountTextWidget.TextColor = ActiveTheme.Instance.PrimaryTextColor; - layerCountTextWidget.VAnchor = VAnchor.ParentCenter; - layerCountTextWidget.AutoExpandBoundsToText = true; - layerCountTextWidget.Margin = new BorderDouble(5, 0); + layerCountTextWidget = new TextWidget("/1____", 12) + { + TextColor = ActiveTheme.Instance.PrimaryTextColor, + VAnchor = VAnchor.ParentCenter, + AutoExpandBoundsToText = true, + Margin = new BorderDouble(5, 0) + }; this.AddChild(layerCountTextWidget); - nextLayerButton = textImageButtonFactory.Generate(">>"); - nextLayerButton.Click += nextLayer_ButtonClick; + var nextLayerButton = buttonFactory.Generate(">>"); + nextLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); + }; this.AddChild(nextLayerButton); } - private void nextLayer_ButtonClick(object sender, EventArgs mouseEvent) - { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); - } - - private void prevLayer_ButtonClick(object sender, EventArgs mouseEvent) - { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); - } - + public override void OnDraw(Graphics2D graphics2D) { if (gcodeViewWidget.LoadedGCode != null) { layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, gcodeViewWidget.LoadedGCode.NumChangesInZ.ToString()); } + base.OnDraw(graphics2D); } } From c88e63361e36f6adcb01ada8ad1598f8aa0e9953 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 08:54:29 -0700 Subject: [PATCH 04/18] Extract LayerNavigation class to file --- MatterControl.csproj | 1 + PartPreviewWindow/LayerNavigationWidget.cs | 80 ++++++++++++++++++++++ PartPreviewWindow/ViewGcodeBasic.cs | 46 ------------- 3 files changed, 81 insertions(+), 46 deletions(-) create mode 100644 PartPreviewWindow/LayerNavigationWidget.cs diff --git a/MatterControl.csproj b/MatterControl.csproj index 05bf214cb..7d99859e9 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -207,6 +207,7 @@ + diff --git a/PartPreviewWindow/LayerNavigationWidget.cs b/PartPreviewWindow/LayerNavigationWidget.cs new file mode 100644 index 000000000..eafbb1354 --- /dev/null +++ b/PartPreviewWindow/LayerNavigationWidget.cs @@ -0,0 +1,80 @@ +/* +Copyright (c) 2017, Lars Brubaker, 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.Agg; +using MatterHackers.Agg.UI; + +namespace MatterHackers.MatterControl.PartPreviewWindow +{ + public class LayerNavigationWidget : FlowLayoutWidget + { + private TextWidget layerCountTextWidget; + private ViewGcodeWidget gcodeViewWidget; + + public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) + : base(FlowDirection.LeftToRight) + { + this.gcodeViewWidget = gcodeViewWidget; + + var prevLayerButton = buttonFactory.Generate("<<"); + prevLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); + }; + this.AddChild(prevLayerButton); + + layerCountTextWidget = new TextWidget("/1____", 12) + { + TextColor = ActiveTheme.Instance.PrimaryTextColor, + VAnchor = VAnchor.ParentCenter, + AutoExpandBoundsToText = true, + Margin = new BorderDouble(5, 0) + }; + this.AddChild(layerCountTextWidget); + + var nextLayerButton = buttonFactory.Generate(">>"); + nextLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); + }; + this.AddChild(nextLayerButton); + } + + + public override void OnDraw(Graphics2D graphics2D) + { + if (gcodeViewWidget.LoadedGCode != null) + { + layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, gcodeViewWidget.LoadedGCode.NumChangesInZ.ToString()); + } + + base.OnDraw(graphics2D); + } + } +} diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index fd54fefe2..b5bc7f18a 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -1137,50 +1137,4 @@ namespace MatterHackers.MatterControl.PartPreviewWindow gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); } } - - public class LayerNavigationWidget : FlowLayoutWidget - { - private TextWidget layerCountTextWidget; - private ViewGcodeWidget gcodeViewWidget; - - public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) - : base(FlowDirection.LeftToRight) - { - this.gcodeViewWidget = gcodeViewWidget; - - var prevLayerButton = buttonFactory.Generate("<<"); - prevLayerButton.Click += (s, e) => - { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); - }; - this.AddChild(prevLayerButton); - - layerCountTextWidget = new TextWidget("/1____", 12) - { - TextColor = ActiveTheme.Instance.PrimaryTextColor, - VAnchor = VAnchor.ParentCenter, - AutoExpandBoundsToText = true, - Margin = new BorderDouble(5, 0) - }; - this.AddChild(layerCountTextWidget); - - var nextLayerButton = buttonFactory.Generate(">>"); - nextLayerButton.Click += (s, e) => - { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); - }; - this.AddChild(nextLayerButton); - } - - - public override void OnDraw(Graphics2D graphics2D) - { - if (gcodeViewWidget.LoadedGCode != null) - { - layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, gcodeViewWidget.LoadedGCode.NumChangesInZ.ToString()); - } - - base.OnDraw(graphics2D); - } - } } From 0f47dc4ef342310b3dac05e29d62365b2aedcc26 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 08:59:14 -0700 Subject: [PATCH 05/18] Extract button factory to model, use lambda events & initializer pattern --- PartPreviewWindow/ViewGcodeBasic.cs | 63 +++++++++++------------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index b5bc7f18a..6817ed720 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -966,7 +966,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow viewControlsToggle.Visible = true; setLayerWidget?.Close(); - setLayerWidget = new SetLayerWidget(gcodeViewWidget); + setLayerWidget = new SetLayerWidget(gcodeViewWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); setLayerWidget.VAnchor = Agg.UI.VAnchor.ParentTop; layerSelectionButtonsPanel.AddChild(setLayerWidget); @@ -1092,49 +1092,34 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public class SetLayerWidget : FlowLayoutWidget { - private NumberEdit editCurrentLayerIndex; - private Button setLayerButton; - private ViewGcodeWidget gcodeViewWidget; - private TextImageButtonFactory textImageButtonFactory = new TextImageButtonFactory(); - - public SetLayerWidget(ViewGcodeWidget gcodeViewWidget) + public SetLayerWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) : base(FlowDirection.LeftToRight) { - this.gcodeViewWidget = gcodeViewWidget; - - textImageButtonFactory.normalTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.hoverTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.disabledTextColor = ActiveTheme.Instance.PrimaryTextColor; - textImageButtonFactory.pressedTextColor = ActiveTheme.Instance.PrimaryTextColor; - - editCurrentLayerIndex = new NumberEdit(1, pixelWidth: 40); - editCurrentLayerIndex.VAnchor = VAnchor.ParentCenter; - editCurrentLayerIndex.Margin = new BorderDouble(5, 0); - editCurrentLayerIndex.EditComplete += editCurrentLayerIndex_EditComplete; - editCurrentLayerIndex.Name = "Current GCode Layer Edit"; + var editCurrentLayerIndex = new NumberEdit(1, pixelWidth: 40) + { + VAnchor = VAnchor.ParentCenter, + Name = "Current GCode Layer Edit", + Margin = new BorderDouble(5, 0) + }; + editCurrentLayerIndex.EditComplete += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + }; this.AddChild(editCurrentLayerIndex); - gcodeViewWidget.ActiveLayerChanged += gcodeViewWidget_ActiveLayerChanged; - setLayerButton = textImageButtonFactory.Generate("Go".Localize()); - setLayerButton.VAnchor = Agg.UI.VAnchor.ParentCenter; - setLayerButton.Click += layerCountTextWidget_EditComplete; + gcodeViewWidget.ActiveLayerChanged += (s, e) => + { + editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + }; + + var setLayerButton = buttonFactory.Generate("Go".Localize()); + setLayerButton.VAnchor = VAnchor.ParentCenter; + setLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + }; this.AddChild(setLayerButton); } - - private void gcodeViewWidget_ActiveLayerChanged(object sender, EventArgs e) - { - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; - } - - private void editCurrentLayerIndex_EditComplete(object sender, EventArgs e) - { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; - } - - private void layerCountTextWidget_EditComplete(object sender, EventArgs e) - { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); - } } } From 156f44dce8eaa688b0be08c1865bc841b20d1c0f Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 09:00:13 -0700 Subject: [PATCH 06/18] Extract SetLayer class to file --- MatterControl.csproj | 1 + PartPreviewWindow/SetLayerWidget.cs | 68 +++++++++++++++++++++++++++++ PartPreviewWindow/ViewGcodeBasic.cs | 33 -------------- 3 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 PartPreviewWindow/SetLayerWidget.cs diff --git a/MatterControl.csproj b/MatterControl.csproj index 7d99859e9..fbd4675e7 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -209,6 +209,7 @@ + diff --git a/PartPreviewWindow/SetLayerWidget.cs b/PartPreviewWindow/SetLayerWidget.cs new file mode 100644 index 000000000..4d156de84 --- /dev/null +++ b/PartPreviewWindow/SetLayerWidget.cs @@ -0,0 +1,68 @@ +/* +Copyright (c) 2017, Lars Brubaker, 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.Agg; +using MatterHackers.Agg.UI; +using MatterHackers.Localizations; + +namespace MatterHackers.MatterControl.PartPreviewWindow +{ + public class SetLayerWidget : FlowLayoutWidget + { + public SetLayerWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) + : base(FlowDirection.LeftToRight) + { + var editCurrentLayerIndex = new NumberEdit(1, pixelWidth: 40) + { + VAnchor = VAnchor.ParentCenter, + Name = "Current GCode Layer Edit", + Margin = new BorderDouble(5, 0) + }; + editCurrentLayerIndex.EditComplete += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + }; + this.AddChild(editCurrentLayerIndex); + + gcodeViewWidget.ActiveLayerChanged += (s, e) => + { + editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + }; + + var setLayerButton = buttonFactory.Generate("Go".Localize()); + setLayerButton.VAnchor = VAnchor.ParentCenter; + setLayerButton.Click += (s, e) => + { + gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + }; + this.AddChild(setLayerButton); + } + } +} diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 6817ed720..9023b2b7f 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -1089,37 +1089,4 @@ namespace MatterHackers.MatterControl.PartPreviewWindow startedSliceFromGenerateButton = false; } } - - public class SetLayerWidget : FlowLayoutWidget - { - public SetLayerWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) - : base(FlowDirection.LeftToRight) - { - var editCurrentLayerIndex = new NumberEdit(1, pixelWidth: 40) - { - VAnchor = VAnchor.ParentCenter, - Name = "Current GCode Layer Edit", - Margin = new BorderDouble(5, 0) - }; - editCurrentLayerIndex.EditComplete += (s, e) => - { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; - }; - this.AddChild(editCurrentLayerIndex); - - gcodeViewWidget.ActiveLayerChanged += (s, e) => - { - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; - }; - - var setLayerButton = buttonFactory.Generate("Go".Localize()); - setLayerButton.VAnchor = VAnchor.ParentCenter; - setLayerButton.Click += (s, e) => - { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); - }; - this.AddChild(setLayerButton); - } - } } From 7808ffe221771405490d37c3fbf7091db03fc8c3 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 09:05:49 -0700 Subject: [PATCH 07/18] Remove tree processing in OnDraw for keyboard hooks --- PartPreviewWindow/ViewGcodeBasic.cs | 55 ----------------------------- 1 file changed, 55 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 9023b2b7f..f8d1704f4 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -815,8 +815,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return gcodeViewWidget; } - private GuiWidget widgetThatHasKeyDownHooked = null; - public override void OnDraw(Graphics2D graphics2D) { bool printerIsRunningPrint = PrinterConnection.Instance.PrinterIsPaused || PrinterConnection.Instance.PrinterIsPrinting; @@ -826,8 +824,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow SetAnimationPosition(); } - EnsureKeyDownHooked(); - if (partToStartLoadingOnFirstDraw != null) { gcodeViewWidget.LoadInBackground(partToStartLoadingOnFirstDraw); @@ -836,45 +832,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow base.OnDraw(graphics2D); } - private void EnsureKeyDownHooked() - { - // let's just check that we are still hooked up to our parent window (this is to make pop outs work correctly) - if (widgetThatHasKeyDownHooked != null) - { - GuiWidget topParent = Parent; - while (topParent as SystemWindow == null) - { - topParent = topParent.Parent; - } - - if (topParent != widgetThatHasKeyDownHooked) - { - widgetThatHasKeyDownHooked.KeyDown -= Parent_KeyDown; - widgetThatHasKeyDownHooked = null; - } - } - - if (widgetThatHasKeyDownHooked == null) - { - GuiWidget parent = Parent; - while (parent as SystemWindow == null) - { - parent = parent.Parent; - } - UnHookWidgetThatHasKeyDownHooked(); - parent.KeyDown += Parent_KeyDown; - widgetThatHasKeyDownHooked = parent; - } - } - - private void UnHookWidgetThatHasKeyDownHooked() - { - if (widgetThatHasKeyDownHooked != null) - { - widgetThatHasKeyDownHooked.KeyDown -= Parent_KeyDown; - } - } - private void Parent_KeyDown(object sender, KeyEventArgs keyEvent) { if (keyEvent.KeyCode == Keys.Up) @@ -924,16 +881,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow gcodeProcessingStateInfoText.Text = message; } - private static bool RunningIn32Bit() - { - if (IntPtr.Size == 4) - { - return true; - } - - return false; - } - private void DoneLoadingGCode(object sender, EventArgs e) { SetProcessingMessage(""); @@ -1047,8 +994,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnClosed(ClosedEventArgs e) { - UnHookWidgetThatHasKeyDownHooked(); - unregisterEvents?.Invoke(this, null); if (printItem != null) From 3dd9f8c4d704ba4719293be98a80b24b609e9ef3 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 10:28:33 -0700 Subject: [PATCH 08/18] Use existing button factory, remove unused button factory instances --- PartPreviewWindow/ViewGcodeBasic.cs | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index f8d1704f4..c792bc5e8 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -85,8 +85,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private PartViewMode activeViewMode = PartViewMode.Layers3D; - private TextImageButtonFactory textImageButtonFactory; - private TextImageButtonFactory ExpandMenuOptionFactory; private ApplicationController.View3DConfig options; @@ -94,8 +92,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow : base(viewControls3D) { this.options = ApplicationController.Instance.Options.View3D; - this.textImageButtonFactory = theme.textImageButtonFactory; - this.ExpandMenuOptionFactory = theme.ExpandMenuOptionFactory; this.viewerVolume = viewerVolume; this.bedShape = bedShape; @@ -161,16 +157,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void CreateAndAddChildren() { - var textImageButtonFactory = new TextImageButtonFactory() - { - normalTextColor = ActiveTheme.Instance.PrimaryTextColor, - hoverTextColor = ActiveTheme.Instance.PrimaryTextColor, - disabledTextColor = ActiveTheme.Instance.PrimaryTextColor, - pressedTextColor = ActiveTheme.Instance.PrimaryTextColor - }; - CloseAllChildren(); + var buttonFactory = ApplicationController.Instance.Theme.BreadCrumbButtonFactory; if (meshViewerWidget != null) { meshViewerWidget.Closed -= MeshViewerWidget_Closed; @@ -190,7 +179,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow buttonBottomPanel.Padding = new BorderDouble(3, 3); buttonBottomPanel.BackgroundColor = ActiveTheme.Instance.PrimaryBackgroundColor; - generateGCodeButton = textImageButtonFactory.Generate("Generate".Localize()); + generateGCodeButton = buttonFactory.Generate("Generate".Localize()); generateGCodeButton.Name = "Generate Gcode Button"; generateGCodeButton.Click += (s, e) => { @@ -227,7 +216,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (windowMode == WindowMode.StandAlone) { - Button closeButton = textImageButtonFactory.Generate("Close".Localize()); + Button closeButton = buttonFactory.Generate("Close".Localize()); layerSelectionButtonsPanel.AddChild(closeButton); closeButton.Click += (sender, e) => { @@ -446,9 +435,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private FlowLayoutWidget CreateModelInfo() { - double oldWidth = textImageButtonFactory.FixedWidth; - textImageButtonFactory.FixedWidth = 44 * GuiWidget.DeviceScale; - var modelInfoContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) { Padding = new BorderDouble(5), @@ -510,8 +496,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // TODO: Every time you click Generate we wire up a listener - only when we close do they get released. This is a terrible pattern that has a good chance of creating a high leak scenario. Since RootedEventHandlers are normally only cleared when a widget is closed, we should **only** register them in widget constructors PrinterConnection.Instance.CommunicationStateChanged.RegisterEvent(HookUpGCodeMessagesWhenDonePrinting, ref unregisterEvents); - textImageButtonFactory.FixedWidth = oldWidth; - return modelInfoContainer; } @@ -601,9 +585,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow internal GuiWidget ShowOverflowMenu() { - double oldWidth = textImageButtonFactory.FixedWidth; - textImageButtonFactory.FixedWidth = 44 * GuiWidget.DeviceScale; - var popupContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) { HAnchor = HAnchor.ParentLeftRight, @@ -729,8 +710,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - textImageButtonFactory.FixedWidth = oldWidth; - return popupContainer; } From 847c088ccea0f8803a1a26dc08132d420e68c724 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 10:30:11 -0700 Subject: [PATCH 09/18] Extract loaded GCodeFile from widget to model --- ApplicationView/ApplicationController.cs | 200 +++++++++++---------- PartPreviewWindow/LayerNavigationWidget.cs | 10 +- PartPreviewWindow/ViewGcodeBasic.cs | 33 ++-- PartPreviewWindow/ViewGcodeWidget.cs | 45 ++--- 4 files changed, 155 insertions(+), 133 deletions(-) diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index 02facf458..9b2327f35 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -53,6 +53,7 @@ namespace MatterHackers.MatterControl using Agg.Image; using CustomWidgets; using MatterHackers.DataConverters3D; + using MatterHackers.GCodeVisualizer; using MatterHackers.MatterControl.ConfigurationPage.PrintLeveling; using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.PartPreviewWindow; @@ -61,6 +62,110 @@ namespace MatterHackers.MatterControl using PrintHistory; using SettingsManagement; + public class ApplicationConfig + { + public View3DConfig View3D { get; } = new View3DConfig(); + } + + public class BedConfig + { + public GCodeFile LoadedGCode { get; set; } + } + + public class PrinterConfig + { + public BedConfig BedPlate { get; } = new BedConfig(); + } + + public class View3DConfig + { + public bool RenderGrid + { + get + { + string value = UserSettings.Instance.get("GcodeViewerRenderGrid"); + if (value == null) + { + RenderGrid = true; + return true; + } + return (value == "True"); + } + set + { + UserSettings.Instance.set("GcodeViewerRenderGrid", value.ToString()); + } + } + + public bool RenderMoves + { + get { return (UserSettings.Instance.get("GcodeViewerRenderMoves") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderMoves", value.ToString()); + } + } + + public bool RenderRetractions + { + get { return (UserSettings.Instance.get("GcodeViewerRenderRetractions") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderRetractions", value.ToString()); + } + } + + public bool RenderSpeeds + { + get { return (UserSettings.Instance.get("GcodeViewerRenderSpeeds") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerRenderSpeeds", value.ToString()); + } + } + + public bool SimulateExtrusion + { + get { return (UserSettings.Instance.get("GcodeViewerSimulateExtrusion") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerSimulateExtrusion", value.ToString()); + } + } + + public bool TransparentExtrusion + { + get { return (UserSettings.Instance.get("GcodeViewerTransparentExtrusion") == "True"); } + set + { + UserSettings.Instance.set("GcodeViewerTransparentExtrusion", value.ToString()); + } + } + + public bool HideExtruderOffsets + { + get + { + string value = UserSettings.Instance.get("GcodeViewerHideExtruderOffsets"); + if (value == null) + { + return true; + } + return (value == "True"); + } + set + { + UserSettings.Instance.set("GcodeViewerHideExtruderOffsets", value.ToString()); + } + } + + public bool SyncToPrint + { + get => UserSettings.Instance.get("GcodeViewerHideExtruderOffsets") == "True"; + set => UserSettings.Instance.set("LayerViewSyncToPrint", value.ToString()); + } + } + public class ApplicationController { internal void ClearPlate() @@ -79,100 +184,7 @@ namespace MatterHackers.MatterControl public ThemeConfig Theme { get; set; } = new ThemeConfig(); - public class View3DConfig - { - public bool RenderGrid - { - get - { - string value = UserSettings.Instance.get("GcodeViewerRenderGrid"); - if (value == null) - { - RenderGrid = true; - return true; - } - return (value == "True"); - } - set - { - UserSettings.Instance.set("GcodeViewerRenderGrid", value.ToString()); - } - } - - public bool RenderMoves - { - get { return (UserSettings.Instance.get("GcodeViewerRenderMoves") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderMoves", value.ToString()); - } - } - - public bool RenderRetractions - { - get { return (UserSettings.Instance.get("GcodeViewerRenderRetractions") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderRetractions", value.ToString()); - } - } - - public bool RenderSpeeds - { - get { return (UserSettings.Instance.get("GcodeViewerRenderSpeeds") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerRenderSpeeds", value.ToString()); - } - } - - public bool SimulateExtrusion - { - get { return (UserSettings.Instance.get("GcodeViewerSimulateExtrusion") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerSimulateExtrusion", value.ToString()); - } - } - - public bool TransparentExtrusion - { - get { return (UserSettings.Instance.get("GcodeViewerTransparentExtrusion") == "True"); } - set - { - UserSettings.Instance.set("GcodeViewerTransparentExtrusion", value.ToString()); - } - } - - public bool HideExtruderOffsets - { - get - { - string value = UserSettings.Instance.get("GcodeViewerHideExtruderOffsets"); - if (value == null) - { - return true; - } - return (value == "True"); - } - set - { - UserSettings.Instance.set("GcodeViewerHideExtruderOffsets", value.ToString()); - } - } - - public bool SyncToPrint - { - get => UserSettings.Instance.get("GcodeViewerHideExtruderOffsets") == "True"; - set => UserSettings.Instance.set("LayerViewSyncToPrint", value.ToString()); - } - } - - public class ApplicationConfig - { - public View3DConfig View3D { get; } = new View3DConfig(); - } - + public PrinterConfig Printer { get; } = new PrinterConfig(); public ApplicationConfig Options { get; } = new ApplicationConfig(); diff --git a/PartPreviewWindow/LayerNavigationWidget.cs b/PartPreviewWindow/LayerNavigationWidget.cs index eafbb1354..6282ce062 100644 --- a/PartPreviewWindow/LayerNavigationWidget.cs +++ b/PartPreviewWindow/LayerNavigationWidget.cs @@ -36,12 +36,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { private TextWidget layerCountTextWidget; private ViewGcodeWidget gcodeViewWidget; - + + private PrinterConfig printer; + public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) : base(FlowDirection.LeftToRight) { this.gcodeViewWidget = gcodeViewWidget; + printer = ApplicationController.Instance.Printer; + var prevLayerButton = buttonFactory.Generate("<<"); prevLayerButton.Click += (s, e) => { @@ -69,9 +73,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnDraw(Graphics2D graphics2D) { - if (gcodeViewWidget.LoadedGCode != null) + if (printer.BedPlate.LoadedGCode != null) { - layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, gcodeViewWidget.LoadedGCode.NumChangesInZ.ToString()); + layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, printer.BedPlate.LoadedGCode.NumChangesInZ.ToString()); } base.OnDraw(graphics2D); diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index c792bc5e8..a13472b52 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -85,13 +85,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private PartViewMode activeViewMode = PartViewMode.Layers3D; + private View3DConfig options; - private ApplicationController.View3DConfig options; + private PrinterConfig printer; public ViewGcodeBasic(Vector3 viewerVolume, Vector2 bedCenter, BedShape bedShape, WindowMode windowMode, ViewControls3D viewControls3D, ThemeConfig theme) : base(viewControls3D) { - this.options = ApplicationController.Instance.Options.View3D; + options = ApplicationController.Instance.Options.View3D; + printer = ApplicationController.Instance.Printer; this.viewerVolume = viewerVolume; this.bedShape = bedShape; @@ -113,12 +115,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow ApplicationController.Instance.AdvancedControlsPanelReloading.RegisterEvent((s, e) => gcodeViewWidget?.Clear3DGCode(), ref unregisterEvents); } + private GCodeFile loadedGCode => printer.BedPlate.LoadedGCode; + private void CheckSettingChanged(object sender, EventArgs e) { StringEventArgs stringEvent = e as StringEventArgs; if (stringEvent != null) { - if (gcodeViewWidget?.LoadedGCode != null + if (loadedGCode != null && ( stringEvent.Data == SettingsKey.filament_cost || stringEvent.Data == SettingsKey.filament_diameter @@ -396,7 +400,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void TrackballTumbleWidget_DrawGlContent(object sender, EventArgs e) { - if (gcodeViewWidget?.LoadedGCode == null) + if (loadedGCode == null || gcodeViewWidget.gCodeRenderer == null) { return; } @@ -404,7 +408,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow GCodeRenderer.ExtrusionColor = ActiveTheme.Instance.PrimaryAccentColor; GCodeRenderInfo renderInfo = new GCodeRenderInfo(0, - Math.Min(gcodeViewWidget.ActiveLayerIndex + 1, gcodeViewWidget.LoadedGCode.NumChangesInZ), + Math.Min(gcodeViewWidget.ActiveLayerIndex + 1, loadedGCode.NumChangesInZ), gcodeViewWidget.TotalTransform, 1, GetRenderType(), @@ -450,9 +454,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { string timeRemainingText = "---"; - if (gcodeViewWidget != null && gcodeViewWidget.LoadedGCode != null) + if (gcodeViewWidget != null && loadedGCode != null) { - int secondsRemaining = (int)gcodeViewWidget.LoadedGCode.Instruction(0).secondsToEndFromHere; + int secondsRemaining = (int)loadedGCode.Instruction(0).secondsToEndFromHere; int hoursRemaining = (int)(secondsRemaining / (60 * 60)); int minutesRemaining = (int)((secondsRemaining + 30) / 60 - hoursRemaining * 60); // +30 for rounding secondsRemaining = secondsRemaining % 60; @@ -474,7 +478,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // show the filament used modelInfoContainer.AddChild(new TextWidget("Filament Length".Localize() + ":", textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); { - double filamentUsed = gcodeViewWidget.LoadedGCode.GetFilamentUsedMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); + double filamentUsed = loadedGCode.GetFilamentUsedMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); GuiWidget estimatedPrintTime = new TextWidget(string.Format("{0:0.0} mm", filamentUsed), pointSize: 14, textColor: ActiveTheme.Instance.PrimaryTextColor); estimatedPrintTime.Margin = new BorderDouble(0, 9, 0, 3); @@ -483,7 +487,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow modelInfoContainer.AddChild(new TextWidget("Filament Volume".Localize() + ":", textColor: ActiveTheme.Instance.PrimaryTextColor, pointSize: 9)); { - double filamentMm3 = gcodeViewWidget.LoadedGCode.GetFilamentCubicMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); + double filamentMm3 = loadedGCode.GetFilamentCubicMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); GuiWidget estimatedPrintTime = new TextWidget(string.Format("{0:0.00} cm³", filamentMm3 / 1000), pointSize: 14, textColor: ActiveTheme.Instance.PrimaryTextColor); estimatedPrintTime.Margin = new BorderDouble(0, 9, 0, 3); @@ -506,7 +510,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow double filamentDiameter = ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter); double filamentDensity = ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_density); - return gcodeViewWidget.LoadedGCode.GetFilamentWeightGrams(filamentDiameter, filamentDensity); + return loadedGCode.GetFilamentWeightGrams(filamentDiameter, filamentDensity); } } @@ -864,7 +868,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { SetProcessingMessage(""); if (gcodeViewWidget != null - && gcodeViewWidget.LoadedGCode == null) + && loadedGCode == null) { // If we have finished loading the gcode and the source file exists but we don't have any loaded gcode it is because the loader decided to not load it. if (File.Exists(printItem.FileLocation)) @@ -878,12 +882,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } if (gcodeViewWidget != null - && gcodeViewWidget.LoadedGCode != null - && gcodeViewWidget.LoadedGCode.LineCount > 0) + && loadedGCode?.LineCount > 0) { // TODO: Shouldn't we be clearing children from some known container and rebuilding? gradientWidget?.Close(); - gradientWidget = new ColorGradientWidget(gcodeViewWidget.LoadedGCode); + gradientWidget = new ColorGradientWidget(loadedGCode); AddChild(gradientWidget); gradientWidget.Visible = false; @@ -903,7 +906,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow layerSelectionButtonsPanel.AddChild(navigationWidget); selectLayerSlider?.Close(); - selectLayerSlider = new SolidSlider(new Vector2(), sliderWidth, 0, gcodeViewWidget.LoadedGCode.NumChangesInZ - 1, Orientation.Vertical); + selectLayerSlider = new SolidSlider(new Vector2(), sliderWidth, 0, loadedGCode.NumChangesInZ - 1, Orientation.Vertical); selectLayerSlider.ValueChanged += new EventHandler(selectLayerSlider_ValueChanged); gcodeViewWidget.ActiveLayerChanged += new EventHandler(gcodeViewWidget_ActiveLayerChanged); AddChild(selectLayerSlider); diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/ViewGcodeWidget.cs index 3aa6b820e..1177d589c 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/ViewGcodeWidget.cs @@ -88,11 +88,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private Vector2 unscaledRenderOffset = new Vector2(0, 0); - public GCodeRenderer gCodeRenderer; + public GCodeRenderer gCodeRenderer { get; private set; } public event EventHandler ActiveLayerChanged; - public GCodeFile LoadedGCode { get; set; } + private GCodeFile loadedGCode => printer.BedPlate.LoadedGCode; public int ActiveLayerIndex { @@ -111,9 +111,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { activeLayerIndex = 0; } - else if (activeLayerIndex >= LoadedGCode.NumChangesInZ) + else if (activeLayerIndex >= loadedGCode.NumChangesInZ) { - activeLayerIndex = LoadedGCode.NumChangesInZ - 1; + activeLayerIndex = loadedGCode.NumChangesInZ - 1; } Invalidate(); @@ -124,10 +124,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private ReportProgressRatio progressReporter; - private ApplicationController.View3DConfig options; + private View3DConfig options; + private PrinterConfig printer; + public ViewGcodeWidget(Vector2 gridSizeMm, Vector2 gridCenterMm, ReportProgressRatio progressReporter) { - this.options = ApplicationController.Instance.Options.View3D; + options = ApplicationController.Instance.Options.View3D; + printer = ApplicationController.Instance.Printer; + this.progressReporter = progressReporter; this.gridSizeMm = gridSizeMm; this.gridCenterMm = gridCenterMm; @@ -139,15 +143,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void SetInitalLayer() { activeLayerIndex = 0; - if (LoadedGCode.LineCount > 0) + if (loadedGCode.LineCount > 0) { int firstExtrusionIndex = 0; - Vector3 lastPosition = LoadedGCode.Instruction(0).Position; - double ePosition = LoadedGCode.Instruction(0).EPosition; + Vector3 lastPosition = loadedGCode.Instruction(0).Position; + double ePosition = loadedGCode.Instruction(0).EPosition; // let's find the first layer that has extrusion if possible and go to that - for (int i = 1; i < LoadedGCode.LineCount; i++) + for (int i = 1; i < loadedGCode.LineCount; i++) { - PrinterMachineInstruction currentInstruction = LoadedGCode.Instruction(i); + PrinterMachineInstruction currentInstruction = loadedGCode.Instruction(i); if (currentInstruction.EPosition > ePosition && lastPosition != currentInstruction.Position) { firstExtrusionIndex = i; @@ -159,9 +163,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (firstExtrusionIndex > 0) { - for (int layerIndex = 0; layerIndex < LoadedGCode.NumChangesInZ; layerIndex++) + for (int layerIndex = 0; layerIndex < loadedGCode.NumChangesInZ; layerIndex++) { - if (firstExtrusionIndex < LoadedGCode.GetInstructionIndexAtLayer(layerIndex)) + if (firstExtrusionIndex < loadedGCode.GetInstructionIndexAtLayer(layerIndex)) { activeLayerIndex = Math.Max(0, layerIndex - 1); break; @@ -185,7 +189,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnDraw(Graphics2D graphics2D) { - if (LoadedGCode != null) + if (loadedGCode != null) { //using (new PerformanceTimer("GCode Timer", "Total")) { @@ -444,10 +448,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public async void LoadInBackground(string gcodePathAndFileName) { - var loadedGCode = await GCodeFileLoaded.LoadInBackground(gcodePathAndFileName, this.progressReporter); - this.LoadedGCode = loadedGCode; + printer.BedPlate.LoadedGCode = await GCodeFileLoaded.LoadInBackground(gcodePathAndFileName, this.progressReporter); - if (this.LoadedGCode == null) + if (loadedGCode == null) { this.AddChild(new TextWidget("Not a valid GCode file.".Localize()) { @@ -462,7 +465,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow CenterPartInView(); } - gCodeRenderer = new GCodeRenderer(this.LoadedGCode); + gCodeRenderer = new GCodeRenderer(loadedGCode); if (ActiveSliceSettings.Instance.PrinterSelected) { @@ -525,10 +528,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public void CenterPartInView() { - if (LoadedGCode != null) + if (loadedGCode != null) { - RectangleDouble partBounds = LoadedGCode.GetBounds(); - Vector2 weightedCenter = LoadedGCode.GetWeightedCenter(); + RectangleDouble partBounds = loadedGCode.GetBounds(); + Vector2 weightedCenter = loadedGCode.GetWeightedCenter(); unscaledRenderOffset = -weightedCenter; layerScale = Math.Min(Height / partBounds.Height, Width / partBounds.Width); From 8b3bc325cd09d2eb8ef932397126456ab15c11a2 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 10:38:08 -0700 Subject: [PATCH 10/18] Extract GCodeRenderer from widget to model --- ApplicationView/ApplicationController.cs | 3 +++ PartPreviewWindow/ViewGcodeBasic.cs | 4 ++-- PartPreviewWindow/ViewGcodeWidget.cs | 27 +++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ApplicationView/ApplicationController.cs b/ApplicationView/ApplicationController.cs index 9b2327f35..b21fd4d5c 100644 --- a/ApplicationView/ApplicationController.cs +++ b/ApplicationView/ApplicationController.cs @@ -70,6 +70,9 @@ namespace MatterHackers.MatterControl public class BedConfig { public GCodeFile LoadedGCode { get; set; } + + // TODO: Make assignment private, wire up post slicing initialization here + public GCodeRenderer GCodeRenderer { get; set; } } public class PrinterConfig diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index a13472b52..ce001ef5d 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -400,7 +400,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void TrackballTumbleWidget_DrawGlContent(object sender, EventArgs e) { - if (loadedGCode == null || gcodeViewWidget.gCodeRenderer == null) + if (loadedGCode == null || printer.BedPlate.GCodeRenderer == null) { return; } @@ -417,7 +417,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, MeshViewerWidget.GetMaterialColor); - gcodeViewWidget.gCodeRenderer.Render3D(renderInfo); + printer.BedPlate.GCodeRenderer.Render3D(renderInfo); } private void SetAnimationPosition() diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/ViewGcodeWidget.cs index 1177d589c..849a2ca23 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/ViewGcodeWidget.cs @@ -88,8 +88,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private Vector2 unscaledRenderOffset = new Vector2(0, 0); - public GCodeRenderer gCodeRenderer { get; private set; } - public event EventHandler ActiveLayerChanged; private GCodeFile loadedGCode => printer.BedPlate.LoadedGCode; @@ -107,7 +105,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { activeLayerIndex = value; - if (gCodeRenderer == null || activeLayerIndex < 0) + if (printer.BedPlate.GCodeRenderer == null || activeLayerIndex < 0) { activeLayerIndex = 0; } @@ -177,9 +175,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow internal void Clear3DGCode() { - if (gCodeRenderer != null) + var renderer = printer.BedPlate.GCodeRenderer; + if (renderer != null) { - gCodeRenderer.Clear3DGCode(); + renderer.Clear3DGCode(); this.Invalidate(); } } @@ -223,7 +222,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow //using (new PerformanceTimer("GCode Timer", "Render")) { - gCodeRenderer?.Render(graphics2D, renderInfo); + printer.BedPlate.GCodeRenderer?.Render(graphics2D, renderInfo); } } } @@ -446,6 +445,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } + // TODO: The bulk of this should move to the data model rather than in this widget public async void LoadInBackground(string gcodePathAndFileName) { printer.BedPlate.LoadedGCode = await GCodeFileLoaded.LoadInBackground(gcodePathAndFileName, this.progressReporter); @@ -465,7 +465,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow CenterPartInView(); } - gCodeRenderer = new GCodeRenderer(loadedGCode); + printer.BedPlate.GCodeRenderer = new GCodeRenderer(loadedGCode); if (ActiveSliceSettings.Instance.PrinterSelected) { @@ -481,14 +481,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow try { // TODO: Why call this then throw away the result? What does calling initialize the otherwise would be invalid? - gCodeRenderer.GCodeFileToDraw?.GetFilamentUsedMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); + printer.BedPlate.GCodeRenderer.GCodeFileToDraw?.GetFilamentUsedMm(ActiveSliceSettings.Instance.GetValue(SettingsKey.filament_diameter)); } catch (Exception ex) { Debug.Print(ex.Message); GuiWidget.BreakInDebugger(); } - gCodeRenderer.CreateFeaturesForLayerIfRequired(0); + + printer.BedPlate.GCodeRenderer.CreateFeaturesForLayerIfRequired(0); }); DoneLoading?.Invoke(this, null); @@ -496,11 +497,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnClosed(ClosedEventArgs e) { - if (gCodeRenderer != null) - { - gCodeRenderer.Dispose(); - } - + printer.BedPlate.GCodeRenderer?.Dispose(); base.OnClosed(e); } @@ -519,7 +516,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { layerScale = layerScale * (Width / oldWidth); } - else if (gCodeRenderer != null) + else if (printer.BedPlate.GCodeRenderer != null) { CenterPartInView(); } From 1339f7573fe301655959de97ce8846eff6258970 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 10:45:02 -0700 Subject: [PATCH 11/18] Remove widget proxy to Clear3DGCode, call directly on model --- PartPreviewWindow/ViewGcodeBasic.cs | 6 ++++-- PartPreviewWindow/ViewGcodeWidget.cs | 16 +++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index ce001ef5d..0fa17614e 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -112,7 +112,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow CreateAndAddChildren(); ActiveSliceSettings.SettingChanged.RegisterEvent(CheckSettingChanged, ref unregisterEvents); - ApplicationController.Instance.AdvancedControlsPanelReloading.RegisterEvent((s, e) => gcodeViewWidget?.Clear3DGCode(), ref unregisterEvents); + + // TODO: Why do we clear GCode on AdvancedControlsPanelReloading - assume some slice settings should invalidate. If so, code should be more specific and bound to slice settings changed + ApplicationController.Instance.AdvancedControlsPanelReloading.RegisterEvent((s, e) => printer.BedPlate.GCodeRenderer?.Clear3DGCode(), ref unregisterEvents); } private GCodeFile loadedGCode => printer.BedPlate.LoadedGCode; @@ -154,7 +156,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } else if (stringEvent.Data == "extruder_offset") { - gcodeViewWidget.Clear3DGCode(); + printer.BedPlate.GCodeRenderer.Clear3DGCode(); } } } diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/ViewGcodeWidget.cs index 849a2ca23..cfc286c16 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/ViewGcodeWidget.cs @@ -133,9 +133,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.progressReporter = progressReporter; this.gridSizeMm = gridSizeMm; this.gridCenterMm = gridCenterMm; - LocalBounds = new RectangleDouble(0, 0, 100, 100); - //DoubleBuffer = true; - AnchorAll(); + + this.LocalBounds = new RectangleDouble(0, 0, 100, 100); + this.AnchorAll(); } private void SetInitalLayer() @@ -173,16 +173,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - internal void Clear3DGCode() - { - var renderer = printer.BedPlate.GCodeRenderer; - if (renderer != null) - { - renderer.Clear3DGCode(); - this.Invalidate(); - } - } - private PathStorage grid = new PathStorage(); static RGBA_Bytes gridColor = new RGBA_Bytes(190, 190, 190, 255); From 4a105fa02feb3bcea654e4caad6d8968456952c7 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 12:24:01 -0700 Subject: [PATCH 12/18] Fix colors for white menu background --- PartPreviewWindow/ViewGcodeBasic.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 0fa17614e..b42ad4ccb 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -591,6 +591,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow internal GuiWidget ShowOverflowMenu() { + var textColor = RGBA_Bytes.Black; + var popupContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) { HAnchor = HAnchor.ParentLeftRight, @@ -599,7 +601,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; // put in a show grid check box - CheckBox showGrid = new CheckBox("Print Bed".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + CheckBox showGrid = new CheckBox("Print Bed".Localize(), textColor: textColor); showGrid.Checked = options.RenderGrid; meshViewerWidget.RenderBed = showGrid.Checked; showGrid.CheckedStateChanged += (sender, e) => @@ -609,7 +611,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow popupContainer.AddChild(showGrid); // put in a show moves checkbox - var showMoves = new CheckBox("Moves".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + var showMoves = new CheckBox("Moves".Localize(), textColor: textColor); showMoves.Checked = options.RenderMoves; showMoves.CheckedStateChanged += (sender, e) => { @@ -618,7 +620,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow popupContainer.AddChild(showMoves); // put in a show Retractions checkbox - CheckBox showRetractions = new CheckBox("Retractions".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + CheckBox showRetractions = new CheckBox("Retractions".Localize(), textColor: textColor); showRetractions.Checked = options.RenderRetractions; showRetractions.CheckedStateChanged += (sender, e) => { @@ -628,7 +630,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // put in a show speed checkbox - var showSpeeds = new CheckBox("Speeds".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + var showSpeeds = new CheckBox("Speeds".Localize(), textColor: textColor); showSpeeds.Checked = options.RenderSpeeds; //showSpeeds.Checked = gradient.Visible; showSpeeds.CheckedStateChanged += (sender, e) => @@ -640,7 +642,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow popupContainer.AddChild(showSpeeds); // put in a simulate extrusion checkbox - var simulateExtrusion = new CheckBox("Extrusion".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + var simulateExtrusion = new CheckBox("Extrusion".Localize(), textColor: textColor); simulateExtrusion.Checked = options.SimulateExtrusion; simulateExtrusion.CheckedStateChanged += (sender, e) => { @@ -649,7 +651,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow popupContainer.AddChild(simulateExtrusion); // put in a render extrusion transparent checkbox - var transparentExtrusion = new CheckBox("Transparent".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor) + var transparentExtrusion = new CheckBox("Transparent".Localize(), textColor: textColor) { Checked = options.TransparentExtrusion, Margin = new BorderDouble(5, 0, 0, 0), @@ -665,7 +667,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // put in a simulate extrusion checkbox if (ActiveSliceSettings.Instance.GetValue(SettingsKey.extruder_count) > 1) { - CheckBox hideExtruderOffsets = new CheckBox("Hide Offsets", textColor: ActiveTheme.Instance.PrimaryTextColor); + CheckBox hideExtruderOffsets = new CheckBox("Hide Offsets", textColor: textColor); hideExtruderOffsets.Checked = options.HideExtruderOffsets; hideExtruderOffsets.CheckedStateChanged += (sender, e) => { @@ -687,7 +689,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Put in the sync to print checkbox if (windowMode == WindowMode.Embeded) { - var syncToPrint = new CheckBox("Sync To Print".Localize(), textColor: ActiveTheme.Instance.PrimaryTextColor); + var syncToPrint = new CheckBox("Sync To Print".Localize(), textColor: textColor); syncToPrint.Checked = (UserSettings.Instance.get("LayerViewSyncToPrint") == "True"); syncToPrint.Name = "Sync To Print Checkbox"; syncToPrint.CheckedStateChanged += (s, e) => From 822faa99a129469339c6b38a4d9f19757383acb3 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 12:24:40 -0700 Subject: [PATCH 13/18] Fix 2D/3D view button event listeners --- PartPreviewWindow/ViewGcodeBasic.cs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index b42ad4ccb..c212f70ef 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -330,8 +330,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Visible = false, HAnchor = HAnchor.ParentRight }; - AddChild(viewControlsToggle); - + viewControlsToggle.ViewModeChanged += (s, e) => + { + // Respond to user driven view mode change events and store and switch to the new mode + activeViewMode = e.ViewMode; + SwitchViewModes(); + }; viewControls3D.TransformStateChanged += (s, e) => { switch (e.TransformMode) @@ -356,6 +360,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } }; + this.AddChild(viewControlsToggle); } private void MeshViewerWidget_Closed(object sender, ClosedEventArgs e) @@ -676,16 +681,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow popupContainer.AddChild(hideExtruderOffsets); } - // Respond to user driven view mode change events and store and switch to the new mode - viewControlsToggle.ViewModeChanged += (s, e) => - { - activeViewMode = e.ViewMode; - SwitchViewModes(); - }; - - // Switch to the most recent view mode, defaulting to Layers3D - SwitchViewModes(); - // Put in the sync to print checkbox if (windowMode == WindowMode.Embeded) { @@ -933,6 +928,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.AddChild(CreateModelInfo()); + // Switch to the most recent view mode, defaulting to Layers3D + SwitchViewModes(); + meshViewerWidget.partProcessingInfo.Visible = false; } } From 848708c36cc71e2272ccd5fc1ac36f2661a34cf7 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 12:35:02 -0700 Subject: [PATCH 14/18] Revise ViewGCodeWidget naming to clarify behavior --- MatterControl.csproj | 2 +- .../{ViewGcodeWidget.cs => GCode2DWidget.cs} | 6 +- PartPreviewWindow/LayerNavigationWidget.cs | 12 +-- PartPreviewWindow/SetLayerWidget.cs | 12 +-- PartPreviewWindow/ViewGcodeBasic.cs | 78 +++++++++---------- 5 files changed, 56 insertions(+), 54 deletions(-) rename PartPreviewWindow/{ViewGcodeWidget.cs => GCode2DWidget.cs} (98%) diff --git a/MatterControl.csproj b/MatterControl.csproj index fbd4675e7..3732ed501 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -242,7 +242,7 @@ - + diff --git a/PartPreviewWindow/ViewGcodeWidget.cs b/PartPreviewWindow/GCode2DWidget.cs similarity index 98% rename from PartPreviewWindow/ViewGcodeWidget.cs rename to PartPreviewWindow/GCode2DWidget.cs index cfc286c16..91a80b1c8 100644 --- a/PartPreviewWindow/ViewGcodeWidget.cs +++ b/PartPreviewWindow/GCode2DWidget.cs @@ -44,7 +44,7 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow { - public class ViewGcodeWidget : GuiWidget + public class GCode2DWidget : GuiWidget { public event EventHandler DoneLoading; @@ -125,7 +125,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private View3DConfig options; private PrinterConfig printer; - public ViewGcodeWidget(Vector2 gridSizeMm, Vector2 gridCenterMm, ReportProgressRatio progressReporter) + public GCode2DWidget(Vector2 gridSizeMm, Vector2 gridCenterMm, ReportProgressRatio progressReporter) { options = ApplicationController.Instance.Options.View3D; printer = ApplicationController.Instance.Printer; @@ -214,6 +214,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { printer.BedPlate.GCodeRenderer?.Render(graphics2D, renderInfo); } + + this.DebugShowBounds = true; } } diff --git a/PartPreviewWindow/LayerNavigationWidget.cs b/PartPreviewWindow/LayerNavigationWidget.cs index 6282ce062..f4bd08a62 100644 --- a/PartPreviewWindow/LayerNavigationWidget.cs +++ b/PartPreviewWindow/LayerNavigationWidget.cs @@ -35,21 +35,21 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public class LayerNavigationWidget : FlowLayoutWidget { private TextWidget layerCountTextWidget; - private ViewGcodeWidget gcodeViewWidget; + private GCode2DWidget gcode2DWidget; private PrinterConfig printer; - public LayerNavigationWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) + public LayerNavigationWidget(GCode2DWidget gcode2DWidget, TextImageButtonFactory buttonFactory) : base(FlowDirection.LeftToRight) { - this.gcodeViewWidget = gcodeViewWidget; + this.gcode2DWidget = gcode2DWidget; printer = ApplicationController.Instance.Printer; var prevLayerButton = buttonFactory.Generate("<<"); prevLayerButton.Click += (s, e) => { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); + gcode2DWidget.ActiveLayerIndex = (gcode2DWidget.ActiveLayerIndex - 1); }; this.AddChild(prevLayerButton); @@ -65,7 +65,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var nextLayerButton = buttonFactory.Generate(">>"); nextLayerButton.Click += (s, e) => { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); + gcode2DWidget.ActiveLayerIndex = (gcode2DWidget.ActiveLayerIndex + 1); }; this.AddChild(nextLayerButton); } @@ -75,7 +75,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (printer.BedPlate.LoadedGCode != null) { - layerCountTextWidget.Text = string.Format("{0} / {1}", gcodeViewWidget.ActiveLayerIndex + 1, printer.BedPlate.LoadedGCode.NumChangesInZ.ToString()); + layerCountTextWidget.Text = string.Format("{0} / {1}", gcode2DWidget.ActiveLayerIndex + 1, printer.BedPlate.LoadedGCode.NumChangesInZ.ToString()); } base.OnDraw(graphics2D); diff --git a/PartPreviewWindow/SetLayerWidget.cs b/PartPreviewWindow/SetLayerWidget.cs index 4d156de84..09e815359 100644 --- a/PartPreviewWindow/SetLayerWidget.cs +++ b/PartPreviewWindow/SetLayerWidget.cs @@ -35,7 +35,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { public class SetLayerWidget : FlowLayoutWidget { - public SetLayerWidget(ViewGcodeWidget gcodeViewWidget, TextImageButtonFactory buttonFactory) + public SetLayerWidget(GCode2DWidget gcode2DWidget, TextImageButtonFactory buttonFactory) : base(FlowDirection.LeftToRight) { var editCurrentLayerIndex = new NumberEdit(1, pixelWidth: 40) @@ -46,21 +46,21 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; editCurrentLayerIndex.EditComplete += (s, e) => { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + gcode2DWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + editCurrentLayerIndex.Value = gcode2DWidget.ActiveLayerIndex + 1; }; this.AddChild(editCurrentLayerIndex); - gcodeViewWidget.ActiveLayerChanged += (s, e) => + gcode2DWidget.ActiveLayerChanged += (s, e) => { - editCurrentLayerIndex.Value = gcodeViewWidget.ActiveLayerIndex + 1; + editCurrentLayerIndex.Value = gcode2DWidget.ActiveLayerIndex + 1; }; var setLayerButton = buttonFactory.Generate("Go".Localize()); setLayerButton.VAnchor = VAnchor.ParentCenter; setLayerButton.Click += (s, e) => { - gcodeViewWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); + gcode2DWidget.ActiveLayerIndex = ((int)editCurrentLayerIndex.Value - 1); }; this.AddChild(setLayerButton); } diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index c212f70ef..6c845723a 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -52,7 +52,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public DoubleSolidSlider layerRenderRatioSlider; private TextWidget gcodeProcessingStateInfoText; - private ViewGcodeWidget gcodeViewWidget; + private GCode2DWidget gcode2DWidget; private PrintItemWrapper printItem => ApplicationController.Instance.ActivePrintItem; private bool startedSliceFromGenerateButton = false; private Button generateGCodeButton; @@ -173,7 +173,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } meshViewerWidget = null; - gcodeViewWidget = null; + gcode2DWidget = null; gcodeProcessingStateInfoText = null; FlowLayoutWidget mainContainerTopToBottom = new FlowLayoutWidget(FlowDirection.TopToBottom); @@ -315,7 +315,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (gcodeDisplayWidget.Visible) { - gcodeViewWidget.CenterPartInView(); + gcode2DWidget.CenterPartInView(); } else { @@ -341,16 +341,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow switch (e.TransformMode) { case ViewControls3DButtons.Translate: - if (gcodeViewWidget != null) + if (gcode2DWidget != null) { - gcodeViewWidget.TransformState = ViewGcodeWidget.ETransformState.Move; + gcode2DWidget.TransformState = GCode2DWidget.ETransformState.Move; } meshViewerWidget.TrackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Translation; break; case ViewControls3DButtons.Scale: - if (gcodeViewWidget != null) + if (gcode2DWidget != null) { - gcodeViewWidget.TransformState = ViewGcodeWidget.ETransformState.Scale; + gcode2DWidget.TransformState = GCode2DWidget.ETransformState.Scale; } meshViewerWidget.TrackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Scale; break; @@ -415,12 +415,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow GCodeRenderer.ExtrusionColor = ActiveTheme.Instance.PrimaryAccentColor; GCodeRenderInfo renderInfo = new GCodeRenderInfo(0, - Math.Min(gcodeViewWidget.ActiveLayerIndex + 1, loadedGCode.NumChangesInZ), - gcodeViewWidget.TotalTransform, + Math.Min(gcode2DWidget.ActiveLayerIndex + 1, loadedGCode.NumChangesInZ), + gcode2DWidget.TotalTransform, 1, GetRenderType(), - gcodeViewWidget.FeatureToStartOnRatio0To1, - gcodeViewWidget.FeatureToEndOnRatio0To1, + gcode2DWidget.FeatureToStartOnRatio0To1, + gcode2DWidget.FeatureToEndOnRatio0To1, new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, MeshViewerWidget.GetMaterialColor); @@ -461,7 +461,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { string timeRemainingText = "---"; - if (gcodeViewWidget != null && loadedGCode != null) + if (gcode2DWidget != null && loadedGCode != null) { int secondsRemaining = (int)loadedGCode.Instruction(0).secondsToEndFromHere; int hoursRemaining = (int)(secondsRemaining / (60 * 60)); @@ -757,11 +757,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow UserSettings.Instance.set("LayerViewDefault", "2D Layer"); // HACK: Getting the Layer2D view to show content only works if CenterPartInView is called after the control is visible and after some cycles have passed - UiThread.RunOnIdle(gcodeViewWidget.CenterPartInView); + UiThread.RunOnIdle(gcode2DWidget.CenterPartInView); } meshViewerWidget.Visible = inLayers3DMode; - gcodeViewWidget.Visible = !inLayers3DMode; + gcode2DWidget.Visible = !inLayers3DMode; } private void HookUpGCodeMessagesWhenDonePrinting(object sender, EventArgs e) @@ -789,12 +789,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private GuiWidget CreateGCodeViewWidget(string pathAndFileName) { - gcodeViewWidget = new ViewGcodeWidget(new Vector2(viewerVolume.x, viewerVolume.y), bedCenter, LoadProgress_Changed); - gcodeViewWidget.DoneLoading += DoneLoadingGCode; - gcodeViewWidget.Visible = (activeViewMode == PartViewMode.Layers2D); + gcode2DWidget = new GCode2DWidget(new Vector2(viewerVolume.x, viewerVolume.y), bedCenter, LoadProgress_Changed); + gcode2DWidget.DoneLoading += DoneLoadingGCode; + gcode2DWidget.Visible = (activeViewMode == PartViewMode.Layers2D); partToStartLoadingOnFirstDraw = pathAndFileName; - return gcodeViewWidget; + return gcode2DWidget; } public override void OnDraw(Graphics2D graphics2D) @@ -808,7 +808,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (partToStartLoadingOnFirstDraw != null) { - gcodeViewWidget.LoadInBackground(partToStartLoadingOnFirstDraw); + gcode2DWidget.LoadInBackground(partToStartLoadingOnFirstDraw); partToStartLoadingOnFirstDraw = null; } base.OnDraw(graphics2D); @@ -818,16 +818,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (keyEvent.KeyCode == Keys.Up) { - if (gcodeViewWidget != null) + if (gcode2DWidget != null) { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex + 1); + gcode2DWidget.ActiveLayerIndex = (gcode2DWidget.ActiveLayerIndex + 1); } } else if (keyEvent.KeyCode == Keys.Down) { - if (gcodeViewWidget != null) + if (gcode2DWidget != null) { - gcodeViewWidget.ActiveLayerIndex = (gcodeViewWidget.ActiveLayerIndex - 1); + gcode2DWidget.ActiveLayerIndex = (gcode2DWidget.ActiveLayerIndex - 1); } } } @@ -866,7 +866,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void DoneLoadingGCode(object sender, EventArgs e) { SetProcessingMessage(""); - if (gcodeViewWidget != null + if (gcode2DWidget != null && loadedGCode == null) { // If we have finished loading the gcode and the source file exists but we don't have any loaded gcode it is because the loader decided to not load it. @@ -880,7 +880,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - if (gcodeViewWidget != null + if (gcode2DWidget != null && loadedGCode?.LineCount > 0) { // TODO: Shouldn't we be clearing children from some known container and rebuilding? @@ -894,20 +894,20 @@ namespace MatterHackers.MatterControl.PartPreviewWindow viewControlsToggle.Visible = true; setLayerWidget?.Close(); - setLayerWidget = new SetLayerWidget(gcodeViewWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); + setLayerWidget = new SetLayerWidget(gcode2DWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); setLayerWidget.VAnchor = Agg.UI.VAnchor.ParentTop; layerSelectionButtonsPanel.AddChild(setLayerWidget); navigationWidget?.Close(); - navigationWidget = new LayerNavigationWidget(gcodeViewWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); + navigationWidget = new LayerNavigationWidget(gcode2DWidget, ApplicationController.Instance.Theme.GCodeLayerButtons); navigationWidget.Margin = new BorderDouble(0, 0, 20, 0); layerSelectionButtonsPanel.AddChild(navigationWidget); selectLayerSlider?.Close(); selectLayerSlider = new SolidSlider(new Vector2(), sliderWidth, 0, loadedGCode.NumChangesInZ - 1, Orientation.Vertical); selectLayerSlider.ValueChanged += new EventHandler(selectLayerSlider_ValueChanged); - gcodeViewWidget.ActiveLayerChanged += new EventHandler(gcodeViewWidget_ActiveLayerChanged); + gcode2DWidget.ActiveLayerChanged += new EventHandler(gcodeViewWidget_ActiveLayerChanged); AddChild(selectLayerSlider); layerRenderRatioSlider?.Close(); @@ -921,8 +921,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow SetSliderSizes(); // let's change the active layer so that it is set to the first layer with data - gcodeViewWidget.ActiveLayerIndex = gcodeViewWidget.ActiveLayerIndex + 1; - gcodeViewWidget.ActiveLayerIndex = gcodeViewWidget.ActiveLayerIndex - 1; + gcode2DWidget.ActiveLayerIndex = gcode2DWidget.ActiveLayerIndex + 1; + gcode2DWidget.ActiveLayerIndex = gcode2DWidget.ActiveLayerIndex - 1; BoundsChanged += new EventHandler(PartPreviewGCode_BoundsChanged); @@ -937,29 +937,29 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void layerStartRenderRatioSlider_ValueChanged(object sender, EventArgs e) { - gcodeViewWidget.FeatureToStartOnRatio0To1 = layerRenderRatioSlider.FirstValue; - gcodeViewWidget.FeatureToEndOnRatio0To1 = layerRenderRatioSlider.SecondValue; - gcodeViewWidget.Invalidate(); + gcode2DWidget.FeatureToStartOnRatio0To1 = layerRenderRatioSlider.FirstValue; + gcode2DWidget.FeatureToEndOnRatio0To1 = layerRenderRatioSlider.SecondValue; + gcode2DWidget.Invalidate(); } private void layerEndRenderRatioSlider_ValueChanged(object sender, EventArgs e) { - gcodeViewWidget.FeatureToStartOnRatio0To1 = layerRenderRatioSlider.FirstValue; - gcodeViewWidget.FeatureToEndOnRatio0To1 = layerRenderRatioSlider.SecondValue; - gcodeViewWidget.Invalidate(); + gcode2DWidget.FeatureToStartOnRatio0To1 = layerRenderRatioSlider.FirstValue; + gcode2DWidget.FeatureToEndOnRatio0To1 = layerRenderRatioSlider.SecondValue; + gcode2DWidget.Invalidate(); } private void gcodeViewWidget_ActiveLayerChanged(object sender, EventArgs e) { - if (gcodeViewWidget.ActiveLayerIndex != (int)(selectLayerSlider.Value + .5)) + if (gcode2DWidget.ActiveLayerIndex != (int)(selectLayerSlider.Value + .5)) { - selectLayerSlider.Value = gcodeViewWidget.ActiveLayerIndex; + selectLayerSlider.Value = gcode2DWidget.ActiveLayerIndex; } } private void selectLayerSlider_ValueChanged(object sender, EventArgs e) { - gcodeViewWidget.ActiveLayerIndex = (int)(selectLayerSlider.Value + .5); + gcode2DWidget.ActiveLayerIndex = (int)(selectLayerSlider.Value + .5); } private void PartPreviewGCode_BoundsChanged(object sender, EventArgs e) From 878d004a501afff54ad1f7eda5e62b1503951c63 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 13:17:43 -0700 Subject: [PATCH 15/18] Remove inherited MeshViewer, push rendering to external mesh viewer --- PartPreviewWindow/PartPreviewContent.cs | 22 ++---- PartPreviewWindow/ViewGcodeBasic.cs | 89 ++++++------------------- 2 files changed, 23 insertions(+), 88 deletions(-) diff --git a/PartPreviewWindow/PartPreviewContent.cs b/PartPreviewWindow/PartPreviewContent.cs index 4f319f368..afbb7e810 100644 --- a/PartPreviewWindow/PartPreviewContent.cs +++ b/PartPreviewWindow/PartPreviewContent.cs @@ -178,7 +178,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow activeSettings.GetValue(SettingsKey.bed_shape), ViewGcodeBasic.WindowMode.Embeded, viewControls3D, - ApplicationController.Instance.Theme); + ApplicationController.Instance.Theme, + modelViewer.meshViewerWidget); gcodeViewer.AnchorAll(); this.gcodeViewer.Visible = false; leftToRight.AddChild(gcodeViewer); @@ -199,9 +200,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { modelViewer.meshViewerWidget.World.RotationMatrix = ApplicationController.Instance.PartPreviewState.RotationMatrix; modelViewer.meshViewerWidget.World.TranslationMatrix = ApplicationController.Instance.PartPreviewState.TranslationMatrix; - - gcodeViewer.meshViewerWidget.World.RotationMatrix = ApplicationController.Instance.PartPreviewState.RotationMatrix; - gcodeViewer.meshViewerWidget.World.TranslationMatrix = ApplicationController.Instance.PartPreviewState.TranslationMatrix; } this.printItem = printItem; @@ -262,19 +260,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public void ToggleView() { - bool layersVisible = gcodeViewer.Visible; - if (layersVisible) - { - // Copy layers tumble state to partpreview - modelViewer.meshViewerWidget.TrackballTumbleWidget.TrackBallController.CopyTransforms(gcodeViewer.meshViewerWidget.TrackballTumbleWidget.TrackBallController); - } - else - { - // Copy partpreview tumble state to layers - gcodeViewer.meshViewerWidget.TrackballTumbleWidget.TrackBallController.CopyTransforms(modelViewer.meshViewerWidget.TrackballTumbleWidget.TrackBallController); - } - - modelViewer.Visible = layersVisible; + bool layersVisible = gcodeViewer.Visible; modelViewer.Visible = layersVisible; gcodeViewer.Visible = !modelViewer.Visible; } @@ -292,7 +278,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnClosed(ClosedEventArgs e) { - var visibleWidget = (gcodeViewer.Visible) ? gcodeViewer.meshViewerWidget : modelViewer.meshViewerWidget; + var visibleWidget = modelViewer.meshViewerWidget; ApplicationController.Instance.PartPreviewState.RotationMatrix = visibleWidget.World.RotationMatrix; ApplicationController.Instance.PartPreviewState.TranslationMatrix = visibleWidget.World.TranslationMatrix; diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index 6c845723a..a142b6fae 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -41,8 +41,10 @@ using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.PartPreviewWindow { - public class ViewGcodeBasic : PartPreview3DWidget + public class ViewGcodeBasic : GuiWidget { + private MeshViewerWidget externalMeshViewer; + public enum WindowMode { Embeded, StandAlone }; public SolidSlider selectLayerSlider; @@ -69,10 +71,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private WindowMode windowMode; private string partToStartLoadingOnFirstDraw = null; - private string gcodeLoading = "Loading G-Code".Localize(); public delegate Vector2 GetSizeFunction(); + private string gcodeLoading = "Loading G-Code".Localize(); private string slicingErrorMessage = "Slicing Error.\nPlease review your slice settings.".Localize(); private string pressGenerateMessage = "Press 'generate' to view layers".Localize(); private string fileNotFoundMessage = "File not found on disk.".Localize(); @@ -88,13 +90,17 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private View3DConfig options; private PrinterConfig printer; + private ViewControls3D viewControls3D; - public ViewGcodeBasic(Vector3 viewerVolume, Vector2 bedCenter, BedShape bedShape, WindowMode windowMode, ViewControls3D viewControls3D, ThemeConfig theme) - : base(viewControls3D) + public ViewGcodeBasic(Vector3 viewerVolume, Vector2 bedCenter, BedShape bedShape, WindowMode windowMode, ViewControls3D viewControls3D, ThemeConfig theme, MeshViewerWidget externalMeshViewer) { + this.externalMeshViewer = externalMeshViewer; + this.externalMeshViewer.TrackballTumbleWidget.DrawGlContent += TrackballTumbleWidget_DrawGlContent; + options = ApplicationController.Instance.Options.View3D; printer = ApplicationController.Instance.Printer; + this.viewControls3D = viewControls3D; this.viewerVolume = viewerVolume; this.bedShape = bedShape; this.bedCenter = bedCenter; @@ -135,26 +141,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow UpdateEstimatedCost(); } - if (stringEvent.Data == SettingsKey.bed_size - || stringEvent.Data == SettingsKey.print_center - || stringEvent.Data == SettingsKey.build_height - || stringEvent.Data == SettingsKey.bed_shape) - { - viewerVolume = new Vector3(ActiveSliceSettings.Instance.GetValue(SettingsKey.bed_size), ActiveSliceSettings.Instance.GetValue(SettingsKey.build_height)); - bedShape = ActiveSliceSettings.Instance.GetValue(SettingsKey.bed_shape); - bedCenter = ActiveSliceSettings.Instance.GetValue(SettingsKey.print_center); - - double buildHeight = ActiveSliceSettings.Instance.GetValue(SettingsKey.build_height); - - UiThread.RunOnIdle(() => - { - meshViewerWidget.CreatePrintBed( - viewerVolume, - bedCenter, - bedShape); - }); - } - else if (stringEvent.Data == "extruder_offset") + if (stringEvent.Data == "extruder_offset") { printer.BedPlate.GCodeRenderer.Clear3DGCode(); } @@ -166,13 +153,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow CloseAllChildren(); var buttonFactory = ApplicationController.Instance.Theme.BreadCrumbButtonFactory; - if (meshViewerWidget != null) - { - meshViewerWidget.Closed -= MeshViewerWidget_Closed; - meshViewerWidget.TrackballTumbleWidget.DrawGlContent -= TrackballTumbleWidget_DrawGlContent; - } - meshViewerWidget = null; + externalMeshViewer = null; gcode2DWidget = null; gcodeProcessingStateInfoText = null; @@ -294,33 +276,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow mainContainerTopToBottom.AddChild(buttonBottomPanel); this.AddChild(mainContainerTopToBottom); - meshViewerWidget = new MeshViewerWidget(viewerVolume, bedCenter, bedShape, "") - { - Visible = (activeViewMode == PartViewMode.Layers3D), - AllowBedRenderingWhenEmpty = true - }; - meshViewerWidget.AnchorAll(); - gcodeDisplayWidget.AddChild(meshViewerWidget); - meshViewerWidget.TrackballTumbleWidget.DrawGlContent += TrackballTumbleWidget_DrawGlContent; - meshViewerWidget.Closed += MeshViewerWidget_Closed; - - // Apply active world view if initialized - if (ApplicationController.Instance.PartPreviewState.RotationMatrix != Matrix4X4.Identity) - { - meshViewerWidget.World.RotationMatrix = ApplicationController.Instance.PartPreviewState.RotationMatrix; - meshViewerWidget.World.TranslationMatrix = ApplicationController.Instance.PartPreviewState.TranslationMatrix; - } - viewControls3D.ResetView += (sender, e) => { if (gcodeDisplayWidget.Visible) { gcode2DWidget.CenterPartInView(); } - else - { - meshViewerWidget.ResetView(); - } }; viewControls3D.ActiveButton = ViewControls3DButtons.Rotate; @@ -345,33 +306,19 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { gcode2DWidget.TransformState = GCode2DWidget.ETransformState.Move; } - meshViewerWidget.TrackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Translation; break; + case ViewControls3DButtons.Scale: if (gcode2DWidget != null) { gcode2DWidget.TransformState = GCode2DWidget.ETransformState.Scale; } - meshViewerWidget.TrackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Scale; break; - case ViewControls3DButtons.Rotate: - meshViewerWidget.TrackballTumbleWidget.TransformState = TrackBallController.MouseDownType.Rotation; - break; - } }; this.AddChild(viewControlsToggle); } - private void MeshViewerWidget_Closed(object sender, ClosedEventArgs e) - { - if (meshViewerWidget.Visible) - { - ApplicationController.Instance.PartPreviewState.RotationMatrix = meshViewerWidget.World.RotationMatrix; - ApplicationController.Instance.PartPreviewState.TranslationMatrix = meshViewerWidget.World.TranslationMatrix; - } - } - private RenderType GetRenderType() { var options = ApplicationController.Instance.Options.View3D; @@ -608,9 +555,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // put in a show grid check box CheckBox showGrid = new CheckBox("Print Bed".Localize(), textColor: textColor); showGrid.Checked = options.RenderGrid; - meshViewerWidget.RenderBed = showGrid.Checked; showGrid.CheckedStateChanged += (sender, e) => { + // TODO: How (if at all) do we disable bed rendering on GCode2D? options.RenderGrid = showGrid.Checked; }; popupContainer.AddChild(showGrid); @@ -760,7 +707,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow UiThread.RunOnIdle(gcode2DWidget.CenterPartInView); } - meshViewerWidget.Visible = inLayers3DMode; gcode2DWidget.Visible = !inLayers3DMode; } @@ -930,8 +876,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow // Switch to the most recent view mode, defaulting to Layers3D SwitchViewModes(); - - meshViewerWidget.partProcessingInfo.Visible = false; } } @@ -980,6 +924,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { unregisterEvents?.Invoke(this, null); + if (externalMeshViewer != null) + { + externalMeshViewer.TrackballTumbleWidget.DrawGlContent -= TrackballTumbleWidget_DrawGlContent; + } + if (printItem != null) { printItem.SlicingOutputMessage -= sliceItem_SlicingOutputMessage; From c17b06d3d0134a877ad6d2929842a5e45fe93147 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Sat, 24 Jun 2017 13:55:12 -0700 Subject: [PATCH 16/18] Tweak GCode view to support rendering widgets on top of MeshViewer --- PartPreviewWindow/PartPreviewContent.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/PartPreviewWindow/PartPreviewContent.cs b/PartPreviewWindow/PartPreviewContent.cs index afbb7e810..9092acc76 100644 --- a/PartPreviewWindow/PartPreviewContent.cs +++ b/PartPreviewWindow/PartPreviewContent.cs @@ -169,7 +169,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow leftToRight.AnchorAll(); topToBottom.AddChild(leftToRight); - leftToRight.AddChild(modelViewer); + var container = new GuiWidget(); + container.AnchorAll(); + container.AddChild(modelViewer); + + leftToRight.AddChild(container); // The slice layers view gcodeViewer = new ViewGcodeBasic( @@ -182,12 +186,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow modelViewer.meshViewerWidget); gcodeViewer.AnchorAll(); this.gcodeViewer.Visible = false; - leftToRight.AddChild(gcodeViewer); + + container.AddChild(gcodeViewer); AddSettingsTabBar(leftToRight, modelViewer); modelViewer.BackgroundColor = ActiveTheme.Instance.TertiaryBackgroundColor; - gcodeViewer.BackgroundColor = ActiveTheme.Instance.TertiaryBackgroundColor; if (ApplicationController.Instance.PartPreviewState.RotationMatrix == Matrix4X4.Identity) { @@ -260,8 +264,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public void ToggleView() { - bool layersVisible = gcodeViewer.Visible; modelViewer.Visible = layersVisible; - gcodeViewer.Visible = !modelViewer.Visible; + gcodeViewer.Visible = !gcodeViewer.Visible; } private async void LoadActivePrintItem() From b7c457309e55325ec41f479ff250189b2e509721 Mon Sep 17 00:00:00 2001 From: John Lewin Date: Mon, 26 Jun 2017 09:45:31 -0700 Subject: [PATCH 17/18] Improve GCode/Model view toggling - Switch models to Wireframe while in GCode view - Conditionally render layers data on visibility of GCode widget - Short term: Clear Scene selection on entering GCode view - Use consistent formatting for GCodeRenderInfo constructor --- PartPreviewWindow/GCode2DWidget.cs | 20 ++++++++++++----- PartPreviewWindow/PartPreviewContent.cs | 1 + PartPreviewWindow/View3D/MeshViewerWidget.cs | 7 ++++-- PartPreviewWindow/View3D/View3DWidget.cs | 23 +++++++++++++++----- PartPreviewWindow/ViewGcodeBasic.cs | 11 +++++++--- Submodules/agg-sharp | 2 +- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/PartPreviewWindow/GCode2DWidget.cs b/PartPreviewWindow/GCode2DWidget.cs index 91a80b1c8..d28df5158 100644 --- a/PartPreviewWindow/GCode2DWidget.cs +++ b/PartPreviewWindow/GCode2DWidget.cs @@ -205,17 +205,25 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - GCodeRenderInfo renderInfo = new GCodeRenderInfo(activeLayerIndex, activeLayerIndex, transform, layerScale, CreateRenderInfo(), - FeatureToStartOnRatio0To1, FeatureToEndOnRatio0To1, - new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, - MeshViewerWidget.GetMaterialColor); + var renderInfo = new GCodeRenderInfo( + activeLayerIndex, + activeLayerIndex, + transform, + layerScale, + CreateRenderInfo(), + FeatureToStartOnRatio0To1, + FeatureToEndOnRatio0To1, + new Vector2[] + { + ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), + ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) + }, + MeshViewerWidget.GetMaterialColor); //using (new PerformanceTimer("GCode Timer", "Render")) { printer.BedPlate.GCodeRenderer?.Render(graphics2D, renderInfo); } - - this.DebugShowBounds = true; } } diff --git a/PartPreviewWindow/PartPreviewContent.cs b/PartPreviewWindow/PartPreviewContent.cs index 9092acc76..7abf89d01 100644 --- a/PartPreviewWindow/PartPreviewContent.cs +++ b/PartPreviewWindow/PartPreviewContent.cs @@ -265,6 +265,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public void ToggleView() { gcodeViewer.Visible = !gcodeViewer.Visible; + modelViewer.ShowSliceLayers = gcodeViewer.Visible; } private async void LoadActivePrintItem() diff --git a/PartPreviewWindow/View3D/MeshViewerWidget.cs b/PartPreviewWindow/View3D/MeshViewerWidget.cs index 231529813..bab5ac792 100644 --- a/PartPreviewWindow/View3D/MeshViewerWidget.cs +++ b/PartPreviewWindow/View3D/MeshViewerWidget.cs @@ -240,7 +240,7 @@ namespace MatterHackers.MeshVisualizer public RenderTypes RenderType { - get { return renderType; } + get => this.IsActive ? renderType : RenderTypes.Wireframe; set { if (renderType != value) @@ -773,6 +773,8 @@ namespace MatterHackers.MeshVisualizer }); } + public bool IsActive { get; set; } = true; + private void DrawObject(IObject3D object3D, Matrix4X4 transform, bool parentSelected) { foreach(MeshAndTransform meshAndTransform in object3D.VisibleMeshes(transform)) @@ -782,11 +784,12 @@ namespace MatterHackers.MeshVisualizer MeshMaterialData meshData = MeshMaterialData.Get(meshAndTransform.MeshData); RGBA_Bytes drawColor = object3D.Color; + if (drawColor.Alpha0To1 == 0) { drawColor = isSelected ? GetSelectedMaterialColor(meshData.MaterialIndex) : GetMaterialColor(meshData.MaterialIndex); } - + GLHelper.Render(meshAndTransform.MeshData, drawColor, meshAndTransform.Matrix, RenderType); } } diff --git a/PartPreviewWindow/View3D/View3DWidget.cs b/PartPreviewWindow/View3D/View3DWidget.cs index 1e30c5556..f3b2fb6f1 100644 --- a/PartPreviewWindow/View3D/View3DWidget.cs +++ b/PartPreviewWindow/View3D/View3DWidget.cs @@ -2285,11 +2285,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow this.saveAsWindow = null; } - private bool scaleQueueMenu_Click() - { - return true; - } - private void SetEditControlsBasedOnPrinterState(object sender, EventArgs e) { if (windowType == WindowMode.Embeded) @@ -2358,6 +2353,23 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Invalidate(); } + private bool showSliceLayers; + public bool ShowSliceLayers + { + get => showSliceLayers; + set + { + showSliceLayers = value; + meshViewerWidget.IsActive = !value; + + if (showSliceLayers) + { + selectedObjectPanel.Visible = false; + Scene.ClearSelection(); + } + } + } + // Before printing persist any changes to disk internal async Task PersistPlateIfNeeded() { @@ -2387,7 +2399,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow SelectedTransformChanged?.Invoke(this, null); } - // ViewControls3D {{ internal GuiWidget ShowOverflowMenu() { diff --git a/PartPreviewWindow/ViewGcodeBasic.cs b/PartPreviewWindow/ViewGcodeBasic.cs index a142b6fae..4c7929568 100644 --- a/PartPreviewWindow/ViewGcodeBasic.cs +++ b/PartPreviewWindow/ViewGcodeBasic.cs @@ -354,21 +354,26 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private void TrackballTumbleWidget_DrawGlContent(object sender, EventArgs e) { - if (loadedGCode == null || printer.BedPlate.GCodeRenderer == null) + if (loadedGCode == null || printer.BedPlate.GCodeRenderer == null || !this.Visible) { return; } GCodeRenderer.ExtrusionColor = ActiveTheme.Instance.PrimaryAccentColor; - GCodeRenderInfo renderInfo = new GCodeRenderInfo(0, + var renderInfo = new GCodeRenderInfo( + 0, Math.Min(gcode2DWidget.ActiveLayerIndex + 1, loadedGCode.NumChangesInZ), gcode2DWidget.TotalTransform, 1, GetRenderType(), gcode2DWidget.FeatureToStartOnRatio0To1, gcode2DWidget.FeatureToEndOnRatio0To1, - new Vector2[] { ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) }, + new Vector2[] + { + ActiveSliceSettings.Instance.Helpers.ExtruderOffset(0), + ActiveSliceSettings.Instance.Helpers.ExtruderOffset(1) + }, MeshViewerWidget.GetMaterialColor); printer.BedPlate.GCodeRenderer.Render3D(renderInfo); diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 706ded619..39c92e01e 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 706ded619b4dd739dcedba938cbd647bb06bf977 +Subproject commit 39c92e01eee0f5334618e54e617ceb3d44d75054 From 352ae2c7dbd7dd0748893a9cacae7b066a6f709c Mon Sep 17 00:00:00 2001 From: John Lewin Date: Mon, 26 Jun 2017 13:52:10 -0700 Subject: [PATCH 18/18] Update public solution --- MatterControl.sln | 129 ++++++++++++++++------------- StaticData/Translations/Master.txt | 3 + 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/MatterControl.sln b/MatterControl.sln index 0f6a8b4b4..ca02dd303 100644 --- a/MatterControl.sln +++ b/MatterControl.sln @@ -5,7 +5,6 @@ VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatterControl", "MatterControl.csproj", "{0B8D6F56-BD7F-4426-B858-D9292B084656}" ProjectSection(ProjectDependencies) = postProject - {F67AE800-B0C7-42A8-836F-597B4E74591C} = {F67AE800-B0C7-42A8-836F-597B4E74591C} {545B6912-77FF-4B34-BA76-6C3D6A32BE6A} = {545B6912-77FF-4B34-BA76-6C3D6A32BE6A} {AE37DE1F-22F7-49EE-8732-FC6BC8DC58D9} = {AE37DE1F-22F7-49EE-8732-FC6BC8DC58D9} {F1653F20-D47D-4F29-8C55-3C835542AF5F} = {F1653F20-D47D-4F29-8C55-3C835542AF5F} @@ -18,7 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatterControl", "MatterCont {74F6BB6C-9D02-4512-A59A-21940E35C532} = {74F6BB6C-9D02-4512-A59A-21940E35C532} {657DBC6D-C3EA-4398-A3FA-DDB73C14F71B} = {657DBC6D-C3EA-4398-A3FA-DDB73C14F71B} {9B062971-A88E-4A3D-B3C9-12B78D15FA66} = {9B062971-A88E-4A3D-B3C9-12B78D15FA66} - {A737BC76-165B-46C6-82B7-8871C7C92942} = {A737BC76-165B-46C6-82B7-8871C7C92942} {CA96058C-1A37-465D-A357-D6D695B13D25} = {CA96058C-1A37-465D-A357-D6D695B13D25} {865172A0-A1A9-49C2-9386-F2FDB4E141B7} = {865172A0-A1A9-49C2-9386-F2FDB4E141B7} {3E4AABA8-D85F-4922-88C6-5C1B2D2308FB} = {3E4AABA8-D85F-4922-88C6-5C1B2D2308FB} @@ -57,12 +55,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Community.CsharpSqlite", "C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RayTracer", "Submodules\agg-sharp\RayTracer\RayTracer.csproj", "{1E01ABE0-B494-4FE4-B0D6-540133286887}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCodeVisualizer", "Submodules\agg-sharp\examples\GCodeVisualizer\GCodeVisualizer.csproj", "{F67AE800-B0C7-42A8-836F-597B4E74591C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialPortCommunication", "Submodules\agg-sharp\SerialPortCommunication\SerialPortCommunication.csproj", "{D3ABF72C-64C2-4E51-A119-E077210FA990}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshViewer", "Submodules\agg-sharp\examples\MeshViewer\MeshViewer.csproj", "{A737BC76-165B-46C6-82B7-8871C7C92942}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Localizations", "Submodules\agg-sharp\Localizations\Localizations.csproj", "{CA96058C-1A37-465D-A357-D6D695B13D25}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessing", "Submodules\agg-sharp\ImageProcessing\ImageProcessing.csproj", "{036BCCBA-52D8-457C-84AE-8821F209FE4A}" @@ -121,6 +115,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSClipperLib", "Submodules\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshThumbnails", "Submodules\agg-sharp\MeshThumbnails\MeshThumbnails\MeshThumbnails.csproj", "{1A901129-C885-425F-8D4B-86698F98A2B4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatterControl.OpenGL", "MatterControl.OpenGL\MatterControl.OpenGL.csproj", "{CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatterControl.Printing", "MatterControl.Printing\MatterControl.Printing.csproj", "{97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -452,32 +450,6 @@ Global {1E01ABE0-B494-4FE4-B0D6-540133286887}.Release64|x64.ActiveCfg = Release64|Any CPU {1E01ABE0-B494-4FE4-B0D6-540133286887}.Release64|x64.Build.0 = Release64|Any CPU {1E01ABE0-B494-4FE4-B0D6-540133286887}.Release64|x86.ActiveCfg = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|x64.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|x64.Build.0 = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug|x86.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|Any CPU.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|Any CPU.Build.0 = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|Mixed Platforms.ActiveCfg = Debug64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|Mixed Platforms.Build.0 = Debug64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|x64.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Debug64|x86.ActiveCfg = Debug|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|Any CPU.Build.0 = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|x64.ActiveCfg = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release|x86.ActiveCfg = Release|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|Any CPU.ActiveCfg = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|Any CPU.Build.0 = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|Mixed Platforms.ActiveCfg = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|Mixed Platforms.Build.0 = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|x64.ActiveCfg = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|x64.Build.0 = Release64|Any CPU - {F67AE800-B0C7-42A8-836F-597B4E74591C}.Release64|x86.ActiveCfg = Release64|Any CPU {D3ABF72C-64C2-4E51-A119-E077210FA990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3ABF72C-64C2-4E51-A119-E077210FA990}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3ABF72C-64C2-4E51-A119-E077210FA990}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -503,31 +475,6 @@ Global {D3ABF72C-64C2-4E51-A119-E077210FA990}.Release64|x64.ActiveCfg = Release64|Any CPU {D3ABF72C-64C2-4E51-A119-E077210FA990}.Release64|x64.Build.0 = Release64|Any CPU {D3ABF72C-64C2-4E51-A119-E077210FA990}.Release64|x86.ActiveCfg = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|x64.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|x64.Build.0 = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug|x86.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|Any CPU.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|Any CPU.Build.0 = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|Mixed Platforms.ActiveCfg = Debug64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|Mixed Platforms.Build.0 = Debug64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|x64.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Debug64|x86.ActiveCfg = Debug|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|Any CPU.Build.0 = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|x64.ActiveCfg = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release|x86.ActiveCfg = Release|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|Any CPU.ActiveCfg = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|Any CPU.Build.0 = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|Mixed Platforms.ActiveCfg = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|Mixed Platforms.Build.0 = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|x64.ActiveCfg = Release64|Any CPU - {A737BC76-165B-46C6-82B7-8871C7C92942}.Release64|x86.ActiveCfg = Release64|Any CPU {CA96058C-1A37-465D-A357-D6D695B13D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA96058C-1A37-465D-A357-D6D695B13D25}.Debug|Any CPU.Build.0 = Debug|Any CPU {CA96058C-1A37-465D-A357-D6D695B13D25}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -1198,6 +1145,70 @@ Global {1A901129-C885-425F-8D4B-86698F98A2B4}.Release64|x64.Build.0 = Release|Any CPU {1A901129-C885-425F-8D4B-86698F98A2B4}.Release64|x86.ActiveCfg = Release|Any CPU {1A901129-C885-425F-8D4B-86698F98A2B4}.Release64|x86.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|x64.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|x64.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug|x86.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|Any CPU.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|Any CPU.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|Mixed Platforms.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|x64.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|x64.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|x86.ActiveCfg = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Debug64|x86.Build.0 = Debug|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|Any CPU.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|x64.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|x64.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|x86.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release|x86.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|Any CPU.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|Any CPU.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|Mixed Platforms.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|Mixed Platforms.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|x64.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|x64.Build.0 = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|x86.ActiveCfg = Release|Any CPU + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}.Release64|x86.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|x64.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|x64.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|x86.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug|x86.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|Any CPU.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|Any CPU.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|Mixed Platforms.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|Mixed Platforms.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|x64.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|x64.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|x86.ActiveCfg = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Debug64|x86.Build.0 = Debug|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|Any CPU.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|x64.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|x64.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|x86.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release|x86.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|Any CPU.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|Any CPU.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|Mixed Platforms.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|Mixed Platforms.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|x64.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|x64.Build.0 = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|x86.ActiveCfg = Release|Any CPU + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}.Release64|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1214,9 +1225,7 @@ Global {C958F745-156E-4BDC-A24A-3721C7BE7B8A} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} {F1653F20-D47D-4F29-8C55-3C835542AF5F} = {FED00F38-E911-45E1-A788-26980E84C3D6} {1E01ABE0-B494-4FE4-B0D6-540133286887} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} - {F67AE800-B0C7-42A8-836F-597B4E74591C} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} {D3ABF72C-64C2-4E51-A119-E077210FA990} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} - {A737BC76-165B-46C6-82B7-8871C7C92942} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} {CA96058C-1A37-465D-A357-D6D695B13D25} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} {036BCCBA-52D8-457C-84AE-8821F209FE4A} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} {9B062971-A88E-4A3D-B3C9-12B78D15FA66} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} @@ -1241,6 +1250,8 @@ Global {23EC3364-7C93-4169-9AB2-7181C66004C0} = {57647FCB-8F00-4C05-972C-3CF6ACD12A63} {2C564BE1-352D-4DDB-8226-F0981F983C60} = {57647FCB-8F00-4C05-972C-3CF6ACD12A63} {1A901129-C885-425F-8D4B-86698F98A2B4} = {2AB9B589-5C98-4C05-BBEA-F97DAE168EAB} + {CBDEEC31-D688-417B-9BF2-F0DB2E4FB268} = {FED00F38-E911-45E1-A788-26980E84C3D6} + {97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F} = {FED00F38-E911-45E1-A788-26980E84C3D6} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = MatterControl.csproj diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index 02f0b108d..060d2dc8e 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -4252,3 +4252,6 @@ Translated:End Threshold: English:Firmware Version: {0} Translated:Firmware Version: {0} +English:Location: 'Settings & Controls' -> 'Settings' -> 'Printer' -> 'Custom G-Code' -> 'Start G-Code' +Translated:Location: 'Settings & Controls' -> 'Settings' -> 'Printer' -> 'Custom G-Code' -> 'Start G-Code' +