Migrate GCodeVisualizer and MeshViewer content into MatterControl

This commit is contained in:
John Lewin 2017-06-21 23:26:20 -07:00
parent 6edbbffabc
commit 7a27433bf0
25 changed files with 4452 additions and 28 deletions

View file

@ -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;
}
}
}

View file

@ -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<float, RGBA_Bytes> speedColorLookup = new SortedList<float, RGBA_Bytes>();
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<float, RGBA_Bytes> keyValue = speedColorLookup.ElementAt(index);
speedColorLookup[keyValue.Key] = RGBA_Floats.FromHSL(fixedColor, .99, .49).GetAsRGBA_Bytes();
}
}
}
return speedColorLookup[speed];
}
}
return RGBA_Bytes.Black;
}
}
}

View file

@ -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<int, RGBA_Bytes> 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<int, RGBA_Bytes> 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;
}
}
}

View file

@ -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<List<int>> featureStartIndex = new List<List<int>>();
private List<List<int>> featureEndIndex = new List<List<int>>();
private List<List<RenderFeatureBase>> renderFeatures = new List<List<RenderFeatureBase>>();
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<RenderFeatureBase>());
}
}
}
public void CreateFeaturesForLayerIfRequired(int layerToCreate)
{
if (extrusionColors == null
&& gCodeFileToDraw != null
&& gCodeFileToDraw.LineCount > 0)
{
extrusionColors = new ExtrusionColors();
HashSet<float> speeds = new HashSet<float>();
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<RenderFeatureBase> 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> colorVertexData,
VectorPOD<int> 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<GCodeVertexBuffer> 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<GCodeVertexBuffer>();
layerVertexBuffer.Capacity = gCodeFileToDraw.NumChangesInZ;
for (int layerIndex = 0; layerIndex < gCodeFileToDraw.NumChangesInZ; layerIndex++)
{
layerVertexBuffer.Add(null);
featureStartIndex.Add(new List<int>());
featureEndIndex.Add(new List<int>());
}
}
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> colorVertexData = new VectorPOD<ColorVertexData>();
VectorPOD<int> vertexIndexArray = new VectorPOD<int>();
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();
}
}
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -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> colorVertexData, VectorPOD<int> indexData, GCodeRenderInfo renderInfo);
public RenderFeatureBase(int extruderIndex)
{
this.extruderIndex = extruderIndex;
}
static public void CreateCylinder(VectorPOD<ColorVertexData> colorVertexData, VectorPOD<int> 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> colorVertexData, VectorPOD<int> 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);
}
}
}
}

View file

@ -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> colorVertexData, VectorPOD<int> 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);
}
}
}
}
}

View file

@ -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> colorVertexData, VectorPOD<int> 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);
}
}
}
}
}

View file

@ -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> colorVertexData, VectorPOD<int> 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);
}
}
}
}
}

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MatterControl.OpenGL</RootNamespace>
<AssemblyName>MatterControl.OpenGL</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GCodeRenderer\ColorVertexData.cs" />
<Compile Include="GCodeRenderer\ExtrusionColors.cs" />
<Compile Include="GCodeRenderer\GCodeRenderer.cs" />
<Compile Include="GCodeRenderer\GCodeRenderInfo.cs" />
<Compile Include="GCodeRenderer\GCodeVertexBuffer.cs" />
<Compile Include="GCodeRenderer\RenderFeatures\RenderFeatureBase.cs" />
<Compile Include="GCodeRenderer\RenderFeatures\RenderFeatureExtrusion.cs" />
<Compile Include="GCodeRenderer\RenderFeatures\RenderFeatureRetract.cs" />
<Compile Include="GCodeRenderer\RenderFeatures\RenderFeatureTravel.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MatterControl.Printing\MatterControl.Printing.csproj">
<Project>{97d5ade3-c1b4-4b46-8a3e-718a4f7f079f}</Project>
<Name>MatterControl.Printing</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\agg\Agg.csproj">
<Project>{657dbc6d-c3ea-4398-a3fa-ddb73c14f71b}</Project>
<Name>Agg</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\Gui\Gui.csproj">
<Project>{74f6bb6c-9d02-4512-a59a-21940e35c532}</Project>
<Name>Gui</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\RenderOpenGl\RenderOpenGl.csproj">
<Project>{545b6912-77ff-4b34-ba76-6c3d6a32be6a}</Project>
<Name>RenderOpenGl</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\VectorMath\VectorMath.csproj">
<Project>{d3e41b4e-bfbb-44ca-94c8-95c00f754fdd}</Project>
<Name>VectorMath</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -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")]

View file

@ -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
}
}

View file

@ -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<int> indexOfChangeInZ = new List<int>();
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<PrinterMachineInstruction> GCodeCommandQueue = new List<PrinterMachineInstruction>();
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<GCodeFileLoaded> 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<string> 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<int> 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<Vector2>(
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<RectangleDouble>(
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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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; }
}
}
}

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MatterControl.Printing</RootNamespace>
<AssemblyName>MatterControl.Printing</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="GCode\GCodeFile.cs" />
<Compile Include="GCode\GCodeFileLoaded.cs" />
<Compile Include="GCode\GCodeFileStreamed.cs" />
<Compile Include="GCode\PrinterMachineInstruction.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Submodules\agg-sharp\agg\Agg.csproj">
<Project>{657dbc6d-c3ea-4398-a3fa-ddb73c14f71b}</Project>
<Name>Agg</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\VectorMath\VectorMath.csproj">
<Project>{d3e41b4e-bfbb-44ca-94c8-95c00f754fdd}</Project>
<Name>VectorMath</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -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")]

View file

@ -209,6 +209,9 @@
<Compile Include="Library\Providers\Zip\ZipMemoryContainer.cs" />
<Compile Include="PartPreviewWindow\OverflowDropdown.cs" />
<Compile Include="PartPreviewWindow\View3D\GridOptionsPanel.cs" />
<Compile Include="PartPreviewWindow\View3D\InteractionVolume.cs" />
<Compile Include="PartPreviewWindow\View3D\MeshViewerWidget.cs" />
<Compile Include="PartPreviewWindow\View3D\MouseEvent3DArgs.cs" />
<Compile Include="PartPreviewWindow\View3D\PrinterActionsBar.cs" />
<Compile Include="PrinterCommunication\Io\NotPrintingStream.cs" />
<Compile Include="Library\Widgets\ListView\ListView.cs" />
@ -474,6 +477,14 @@
<Folder Include="SlicerConfiguration\SettingsEditors\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="MatterControl.OpenGL\MatterControl.OpenGL.csproj">
<Project>{CBDEEC31-D688-417B-9BF2-F0DB2E4FB268}</Project>
<Name>MatterControl.OpenGL</Name>
</ProjectReference>
<ProjectReference Include="MatterControl.Printing\MatterControl.Printing.csproj">
<Project>{97D5ADE3-C1B4-4B46-8A3E-718A4F7F079F}</Project>
<Name>MatterControl.Printing</Name>
</ProjectReference>
<ProjectReference Include="Submodules\agg-sharp\clipper_library\clipper_library.csproj">
<Project>{9B062971-A88E-4A3D-B3C9-12B78D15FA66}</Project>
<Name>clipper_library</Name>
@ -518,10 +529,6 @@
<Project>{D3E41B4E-BFBB-44CA-94C8-95C00F754FDD}</Project>
<Name>VectorMath</Name>
</ProjectReference>
<ProjectReference Include="Submodules\agg-sharp\examples\GCodeVisualizer\GCodeVisualizer.csproj">
<Project>{F67AE800-B0C7-42A8-836F-597B4E74591C}</Project>
<Name>GCodeVisualizer</Name>
</ProjectReference>
<ProjectReference Include="Submodules\agg-sharp\SerialPortCommunication\SerialPortCommunication.csproj">
<Project>{D3ABF72C-64C2-4E51-A119-E077210FA990}</Project>
<Name>SerialPortCommunication</Name>
@ -534,10 +541,6 @@
<Project>{C46CA728-DD2F-4DD1-971A-AAA89D9DFF95}</Project>
<Name>MatterSlice</Name>
</ProjectReference>
<ProjectReference Include="Submodules\agg-sharp\examples\MeshViewer\MeshViewer.csproj">
<Project>{A737BC76-165B-46C6-82B7-8871C7C92942}</Project>
<Name>MeshViewer</Name>
</ProjectReference>
<ProjectReference Include="Submodules\agg-sharp\Tesselate\Tesselate.csproj">
<Project>{AE37DE1F-22F7-49EE-8732-FC6BC8DC58D9}</Project>
<Name>Tesselate</Name>

View file

@ -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);
}
}
}

View file

@ -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<InteractionVolume> interactionVolumes = new List<InteractionVolume>();
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<int, RGBA_Bytes> materialColors = new Dictionary<int, RGBA_Bytes>();
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<MeshGroup> 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<WidgetAndPosition> 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<IPrimitive> uiTraceables = new List<IPrimitive>();
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<IBvhItem> insideBounds = new List<IBvhItem>();
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;
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}

View file

@ -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"))
{

View file

@ -83,6 +83,10 @@
<Project>{0B8D6F56-BD7F-4426-B858-D9292B084656}</Project>
<Name>MatterControl</Name>
</ProjectReference>
<ProjectReference Include="..\..\MatterControl.Printing\MatterControl.Printing.csproj">
<Project>{97d5ade3-c1b4-4b46-8a3e-718a4f7f079f}</Project>
<Name>MatterControl.Printing</Name>
</ProjectReference>
<ProjectReference Include="..\..\PrinterEmulator\PrinterEmulator.csproj">
<Project>{bb58ca42-991b-41b7-bde7-dcd2911df8b9}</Project>
<Name>PrinterEmulator</Name>
@ -95,14 +99,6 @@
<Project>{04667764-dc7b-4b95-aef6-b4e6c87a54e9}</Project>
<Name>DataConverters3D</Name>
</ProjectReference>
<ProjectReference Include="..\..\Submodules\agg-sharp\examples\GCodeVisualizer\GCodeVisualizer.csproj">
<Project>{F67AE800-B0C7-42A8-836F-597B4E74591C}</Project>
<Name>GCodeVisualizer</Name>
</ProjectReference>
<ProjectReference Include="..\..\Submodules\agg-sharp\examples\MeshViewer\MeshViewer.csproj">
<Project>{a737bc76-165b-46c6-82b7-8871c7c92942}</Project>
<Name>MeshViewer</Name>
</ProjectReference>
<ProjectReference Include="..\..\Submodules\agg-sharp\GuiAutomation\GuiAutomation.csproj">
<Project>{E9102310-0029-4D8F-B1E9-88FBA6147D45}</Project>
<Name>GuiAutomation</Name>

View file

@ -166,10 +166,6 @@
<Project>{04667764-DC7B-4B95-AEF6-B4E6C87A54E9}</Project>
<Name>DataConverters3D</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\examples\MeshViewer\MeshViewer.csproj">
<Project>{A737BC76-165B-46C6-82B7-8871C7C92942}</Project>
<Name>MeshViewer</Name>
</ProjectReference>
<ProjectReference Include="..\Submodules\agg-sharp\Gui\Gui.csproj">
<Project>{74F6BB6C-9D02-4512-A59A-21940E35C532}</Project>
<Name>Gui</Name>