Merge pull request #4822 from larsbrubaker/master

master
This commit is contained in:
Lars Brubaker 2020-08-24 07:32:45 -07:00 committed by GitHub
commit 25adba5466
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 238 additions and 97 deletions

View file

@ -130,6 +130,7 @@ namespace MatterHackers.GCodeVisualizer
// this is a retraction
renderFeaturesForLayer.Add(new RenderFeatureRetract(instructionIndex, currentInstruction.Position, eMovement, currentInstruction.ToolIndex, currentInstruction.FeedRate));
}
if (currentInstruction.Line.StartsWith("G10"))
{
renderFeaturesForLayer.Add(new RenderFeatureRetract(instructionIndex, currentInstruction.Position, -1, currentInstruction.ToolIndex, currentInstruction.FeedRate));

View file

@ -46,7 +46,8 @@ namespace MatterHackers.Plugins.EditorTools
{
public class ScaleCornerControl : InteractionVolume
{
public IObject3D ActiveSelectedItem;
public IObject3D ActiveSelectedItem { get; set; }
protected PlaneShape hitPlane;
protected Vector3 initialHitPosition;
protected Mesh minXminYMesh;
@ -54,9 +55,9 @@ namespace MatterHackers.Plugins.EditorTools
protected Matrix4X4 transformOnMouseDown = Matrix4X4.Identity;
protected Matrix4X4 transformAppliedByThis = Matrix4X4.Identity;
private double distToStart => 10 * GuiWidget.DeviceScale;
private double DistToStart => 10 * GuiWidget.DeviceScale;
private double lineLength => 35 * GuiWidget.DeviceScale;
private double LineLength => 35 * GuiWidget.DeviceScale;
private List<Vector2> lines = new List<Vector2>();
private Vector3 originalPointToMove;
@ -384,18 +385,18 @@ namespace MatterHackers.Plugins.EditorTools
lines.Clear();
// left lines
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(xSign * distToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(xSign * (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(xSign * DistToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(xSign * (DistToStart + LineLength) * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(yOtherSide - new Vector3(xSign * distToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(yOtherSide - new Vector3(xSign * (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(yOtherSide - new Vector3(xSign * DistToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(yOtherSide - new Vector3(xSign * (DistToStart + LineLength) * distBetweenPixelsWorldSpace, 0, 0)));
// bottom lines
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(0, ySign * distToStart * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(0, ySign * (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(0, ySign * DistToStart * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(cornerPosition - new Vector3(0, ySign * (DistToStart + LineLength) * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(xOtherSide - new Vector3(0, ySign * distToStart * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(xOtherSide - new Vector3(0, ySign * (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(xOtherSide - new Vector3(0, ySign * DistToStart * distBetweenPixelsWorldSpace, 0)));
lines.Add(InteractionContext.World.GetScreenPosition(xOtherSide - new Vector3(0, ySign * (DistToStart + LineLength) * distBetweenPixelsWorldSpace, 0)));
}
public static Vector3 GetScalingConsideringShiftKey(AxisAlignedBoundingBox originalSelectedBounds,

View file

@ -53,14 +53,17 @@ namespace MatterHackers.Plugins.EditorTools
protected Mesh topScaleMesh;
protected AxisAlignedBoundingBox mouseDownSelectedBounds;
protected Matrix4X4 transformOnMouseDown = Matrix4X4.Identity;
private double distToStart = 5;
private double lineLength = 55;
private double DistToStart => 5 * GuiWidget.DeviceScale;
private double LineLength => 55 * GuiWidget.DeviceScale;
private List<Vector2> lines = new List<Vector2>();
private Vector3 originalPointToMove;
private double selectCubeSize = 7 * GuiWidget.DeviceScale;
private ThemeConfig theme;
private InlineEditControl zValueDisplayInfo;
private bool HadClickOnControl;
private bool hadClickOnControl;
public ScaleTopControl(IInteractionVolumeContext context)
: base(context)
@ -69,7 +72,7 @@ namespace MatterHackers.Plugins.EditorTools
zValueDisplayInfo = new InlineEditControl()
{
ForceHide = () =>
ForceHide = () =>
{
// if the selection changes
if (RootSelection != ActiveSelectedItem)
@ -84,16 +87,14 @@ namespace MatterHackers.Plugins.EditorTools
return true;
}
// if we clicked on the control
if (HadClickOnControl)
if (hadClickOnControl)
{
return false;
}
return false;
}
,
},
GetDisplayString = (value) => "{0:0.0}mm".FormatWith(value)
};
@ -101,7 +102,7 @@ namespace MatterHackers.Plugins.EditorTools
{
if (!zValueDisplayInfo.Visible)
{
HadClickOnControl = false;
hadClickOnControl = false;
}
};
@ -214,7 +215,7 @@ namespace MatterHackers.Plugins.EditorTools
{
if (mouseEvent3D.info != null && InteractionContext.Scene.SelectedItem != null)
{
HadClickOnControl = true;
hadClickOnControl = true;
ActiveSelectedItem = RootSelection;
zValueDisplayInfo.Visible = true;
@ -242,7 +243,7 @@ namespace MatterHackers.Plugins.EditorTools
{
zValueDisplayInfo.Visible = true;
}
else if (!HadClickOnControl)
else if (!hadClickOnControl)
{
zValueDisplayInfo.Visible = false;
}
@ -335,11 +336,11 @@ namespace MatterHackers.Plugins.EditorTools
lines.Clear();
// left lines
lines.Add(InteractionContext.World.GetScreenPosition(topPosition + new Vector3(distToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(new Vector2(lines[0].X + lineLength, lines[0].Y));
lines.Add(InteractionContext.World.GetScreenPosition(topPosition + new Vector3(DistToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(new Vector2(lines[0].X + LineLength, lines[0].Y));
lines.Add(InteractionContext.World.GetScreenPosition(bottomPosition + new Vector3(distToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(new Vector2(lines[2].X + lineLength, lines[2].Y));
lines.Add(InteractionContext.World.GetScreenPosition(bottomPosition + new Vector3(DistToStart * distBetweenPixelsWorldSpace, 0, 0)));
lines.Add(new Vector2(lines[2].X + LineLength, lines[2].Y));
}
private void InteractionLayer_AfterDraw(object sender, DrawEventArgs drawEvent)

View file

@ -272,7 +272,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
return this.Children.FirstOrDefault();
}
}
public static Vector3 GetPositionToAlignTo(IObject3D objectToAlignTo, FaceAlign boundingFacesToAlignTo, Vector3 extraOffset)
{
var positionToAlignTo = default(Vector3);

View file

@ -28,8 +28,10 @@ either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using MatterControl.Printing;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
@ -156,6 +158,52 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
return loadedGCode.GetLayerTop(layerIndex);
}
public static List<List<Vector2>> GetExtrusionsForLayer(this GCodeFile gcode, int layerIndex)
{
var extrusions = new List<List<Vector2>>();
bool addingExtrudePath = false;
List<Vector2> currentExtrudePath = null;
int startRenderIndex = gcode.GetFirstLayerInstruction(layerIndex);
int endRenderIndex = gcode.LineCount - 1;
if (layerIndex < gcode.LayerCount - 1)
{
endRenderIndex = gcode.GetFirstLayerInstruction(layerIndex + 1);
}
for (int instructionIndex = startRenderIndex; instructionIndex < endRenderIndex; instructionIndex++)
{
PrinterMachineInstruction currentInstruction = gcode.Instruction(instructionIndex);
PrinterMachineInstruction previousInstruction = currentInstruction;
if (instructionIndex > 0)
{
previousInstruction = gcode.Instruction(instructionIndex - 1);
}
if (currentInstruction.Position != previousInstruction.Position)
{
if (gcode.IsExtruding(instructionIndex))
{
if (!addingExtrudePath)
{
currentExtrudePath = new List<Vector2>();
extrusions.Add(currentExtrudePath);
addingExtrudePath = true;
}
currentExtrudePath.Add(new Vector2(currentInstruction.Position.X, currentInstruction.Position.Y));
}
else
{
addingExtrudePath = false;
}
}
}
return extrusions;
}
public static string GetLayerFanSpeeds(this GCodeFile loadedGCode, int activeLayerIndex)
{
if (loadedGCode == null || loadedGCode.LayerCount == 0)
@ -164,10 +212,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
int startInstruction = loadedGCode.GetFirstLayerInstruction(activeLayerIndex);
if(activeLayerIndex == 0)
if (activeLayerIndex == 0)
{
startInstruction = 0;
}
int endInstruction = loadedGCode.GetFirstLayerInstruction(activeLayerIndex + 1);
string separator = "";
@ -180,12 +229,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
fanSpeeds += separator + "Off";
separator = ", ";
}
else if(line.StartsWith("M106")) // fan on
else if (line.StartsWith("M106")) // fan on
{
double speed = 0;
if (GCodeFile.GetFirstNumberAfter("M106", line, ref speed, 0, ""))
{
fanSpeeds += separator + $"{speed/255*100:0}%";
fanSpeeds += separator + $"{speed / 255 * 100:0}%";
separator = ", ";
}
}

View file

@ -30,25 +30,27 @@ either expressed or implied, of the FreeBSD Project.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.ConfigurationPage;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class GCodePanel : FlowLayoutWidget
{
private ISceneContext sceneContext;
private ThemeConfig theme;
private PrinterConfig printer;
private PrinterTabPage printerTabPage;
private readonly ISceneContext sceneContext;
private readonly ThemeConfig theme;
private readonly PrinterConfig printer;
private readonly PrinterTabPage printerTabPage;
private SectionWidget speedsWidget;
private GuiWidget loadedGCodeSection;
private readonly GuiWidget loadedGCodeSection;
public GCodePanel(PrinterTabPage printerTabPage, PrinterConfig printer, ISceneContext sceneContext, ThemeConfig theme)
: base (FlowDirection.TopToBottom)
: base(FlowDirection.TopToBottom)
{
this.sceneContext = sceneContext;
this.theme = theme;
@ -143,6 +145,55 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
VAnchor = VAnchor.Fit
});
var copyButton = new TextButton("copy".Localize(), theme, 8)
{
Padding = 5,
Margin = new BorderDouble(0, 0, 15, 0),
VAnchor = VAnchor.Center | VAnchor.Fit,
ToolTipText = "Copy extrusions data".Localize()
};
copyButton.Click += (s, e) =>
{
var output = new StringBuilder();
// copy all the extrusions to the clipboard as paths
var extrusions = printer.Bed.LoadedGCode.GetExtrusionsForLayer(sceneContext.ActiveLayerIndex);
for (int i = 0; i < extrusions.Count; i++)
{
var extrusion = extrusions[i];
var count = extrusion.Count;
for (var j = 0; j < count; j++)
{
var position = extrusion[j];
bool debugTurns = false;
if (debugTurns)
{
var prevPosition = extrusion[(count + j - 1) % count];
var nextPosition = extrusion[(count + j + 1) % count];
var angle = position.GetTurnAmount(prevPosition, nextPosition);
var lengthToPoint = extrusion.LengthTo(j);
var leftPosition = extrusion.GetPositionAt(lengthToPoint - 1.6);
var rightPosition = extrusion.GetPositionAt(lengthToPoint + 1.6);
var nearAngle = position.GetTurnAmount(leftPosition, rightPosition);
var directionNormal = (rightPosition - leftPosition).GetNormal().GetPerpendicularRight();
var delta = Vector2.Dot(directionNormal, position - leftPosition);
output.AppendLine($"{lengthToPoint:0.##}, {angle:0.##}, {position.X:0.##}, {position.Y:0.##}, {delta:0.##}");
}
else
{
// output.AppendLine($"x:{position.X:0.##}, y:{position.Y:0.##}");
output.Append($"x:{position.X:0.##}, y:{position.Y:0.##},");
}
}
output.Append("|");
}
Clipboard.Instance.SetText(output.ToString());
};
loadedGCodeSection.AddChild(
new SectionWidget(
"Layer".Localize(),
@ -154,6 +205,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
},
theme,
serializationKey: "gcode_panel_layer_details",
rightAlignedContent: copyButton,
expanded: true)
{
HAnchor = HAnchor.Stretch,

View file

@ -0,0 +1,78 @@
/*
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 MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.Agg.VertexSource;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class HorizontalTag : GuiWidget
{
private IVertexSource tabShape = null;
public Color TagColor { get; set; }
public override void OnBoundsChanged(EventArgs e)
{
base.OnBoundsChanged(e);
var rect = this.LocalBounds;
var centerY = rect.YCenter;
// Tab - core
var radius = 3.0;
var tabShape2 = new VertexStorage();
tabShape2.MoveTo(rect.Left + radius, rect.Bottom);
tabShape2.curve3(rect.Left, rect.Bottom, rect.Left, rect.Bottom + radius);
tabShape2.LineTo(rect.Left, rect.Top - radius);
tabShape2.curve3(rect.Left, rect.Top, rect.Left + radius, rect.Top);
tabShape2.LineTo(rect.Right - 8, rect.Top);
tabShape2.LineTo(rect.Right, centerY);
tabShape2.LineTo(rect.Right - 8, rect.Bottom);
tabShape2.LineTo(rect.Left, rect.Bottom);
tabShape = new FlattenCurves(tabShape2);
}
public override void OnDrawBackground(Graphics2D graphics2D)
{
base.OnDrawBackground(graphics2D);
if (tabShape != null)
{
graphics2D.Render(tabShape, this.TagColor);
}
}
}
}

View file

@ -38,15 +38,13 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class SliceLayerSelector : GuiWidget
{
public static int SliderWidth { get; } = 10;
public static double SliderWidth { get; } = 10 * GuiWidget.DeviceScale;
private InlineEditControl currentLayerInfo;
private readonly InlineEditControl currentLayerInfo;
private LayerScrollbar layerScrollbar;
private SolidSlider layerSlider;
private double layerInfoHalfHeight;
private PrinterConfig printer;
private readonly LayerScrollbar layerScrollbar;
private readonly double layerInfoHalfHeight;
private readonly PrinterConfig printer;
public SliceLayerSelector(PrinterConfig printer, ThemeConfig theme)
{
@ -57,8 +55,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
HAnchor = HAnchor.Right
});
layerSlider = layerScrollbar.layerSlider;
theme.ApplySliderStyle(layerSlider);
SolidSlider = layerScrollbar.layerSlider;
theme.ApplySliderStyle(SolidSlider);
var tagContainer = new HorizontalTag()
{
@ -66,7 +64,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
VAnchor = VAnchor.Fit,
Padding = new BorderDouble(6, 4, 10, 4),
Margin = new BorderDouble(right: layerScrollbar.Width + layerScrollbar.Margin.Width),
TagColor = (theme.IsDarkTheme) ? theme.Shade : theme.SlightShade
TagColor = theme.IsDarkTheme ? theme.Shade : theme.SlightShade
};
currentLayerInfo = new InlineEditControl("1000")
@ -88,20 +86,20 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
layerInfoHalfHeight = currentLayerInfo.Height / 2;
currentLayerInfo.Visible = false;
layerSlider.ValueChanged += (s, e) =>
SolidSlider.ValueChanged += (s, e) =>
{
currentLayerInfo.StopEditing();
currentLayerInfo.Position = new Vector2(0, (double)(layerSlider.Position.Y + layerSlider.PositionPixelsFromFirstValue - layerInfoHalfHeight));
currentLayerInfo.Position = new Vector2(0, (double)(SolidSlider.Position.Y + SolidSlider.PositionPixelsFromFirstValue - layerInfoHalfHeight));
};
// Set initial position
currentLayerInfo.Position = new Vector2(0, (double)(layerSlider.Position.Y + layerSlider.PositionPixelsFromFirstValue - layerInfoHalfHeight));
currentLayerInfo.Position = new Vector2(0, (double)(SolidSlider.Position.Y + SolidSlider.PositionPixelsFromFirstValue - layerInfoHalfHeight));
printer.Bed.ActiveLayerChanged += SetPositionAndValue;
layerScrollbar.MouseEnter += SetPositionAndValue;
}
public SolidSlider SolidSlider => layerSlider;
public SolidSlider SolidSlider { get; }
public double Maximum
{
@ -118,7 +116,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
public override void OnClosed(EventArgs e)
{
printer.Bed.ActiveLayerChanged -= SetPositionAndValue;
layerSlider.MouseEnter -= SetPositionAndValue;
SolidSlider.MouseEnter -= SetPositionAndValue;
base.OnClosed(e);
}
@ -135,9 +133,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private class LayerScrollbar : FlowLayoutWidget
{
internal SolidSlider layerSlider;
private PrinterConfig printer;
private TextWidget layerCountText;
private TextWidget layerStartText;
private readonly PrinterConfig printer;
private readonly TextWidget layerCountText;
private readonly TextWidget layerStartText;
public LayerScrollbar(PrinterConfig printer, ThemeConfig theme)
: base(FlowDirection.TopToBottom)
@ -151,7 +149,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
};
this.AddChild(layerCountText);
layerSlider = new SolidSlider(new Vector2(), SliderWidth, theme, 0, 1, Orientation.Vertical)
layerSlider = new SolidSlider(default(Vector2), SliderWidth, theme, 0, 1, Orientation.Vertical)
{
HAnchor = HAnchor.Center,
VAnchor = VAnchor.Stretch,
@ -204,7 +202,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
if (layerSlider != null)
{
//layerSlider.OriginRelativeParent = new Vector2(this.Width - 20, 78);
// layerSlider.OriginRelativeParent = new Vector2(this.Width - 20, 78);
layerSlider.TotalWidthInPixels = layerSlider.Height;
}
}
@ -226,43 +224,4 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
}
}
public class HorizontalTag : GuiWidget
{
private IVertexSource tabShape = null;
public Color TagColor { get; set; }
public override void OnBoundsChanged(EventArgs e)
{
base.OnBoundsChanged(e);
var rect = this.LocalBounds;
var centerY = rect.YCenter;
// Tab - core
var radius = 3.0;
var tabShape2 = new VertexStorage();
tabShape2.MoveTo(rect.Left + radius, rect.Bottom);
tabShape2.curve3(rect.Left, rect.Bottom, rect.Left, rect.Bottom + radius);
tabShape2.LineTo(rect.Left, rect.Top - radius);
tabShape2.curve3(rect.Left, rect.Top, rect.Left + radius, rect.Top);
tabShape2.LineTo(rect.Right - 8, rect.Top);
tabShape2.LineTo(rect.Right, centerY);
tabShape2.LineTo(rect.Right - 8, rect.Bottom);
tabShape2.LineTo(rect.Left, rect.Bottom);
tabShape = new FlattenCurves(tabShape2);
}
public override void OnDrawBackground(Graphics2D graphics2D)
{
base.OnDrawBackground(graphics2D);
if (tabShape != null)
{
graphics2D.Render(tabShape, this.TagColor);
}
}
}
}

@ -1 +1 @@
Subproject commit 389ba8e0b825410e527738dec8861c6003120bdf
Subproject commit 34cedef286070a031623c99013a4a9b260f1e966

@ -1 +1 @@
Subproject commit 5c9c8c05102b03ce56a4bd53eaec74f4e1d9347a
Subproject commit 171822042ca5a6df9b99e37c7d0b20443e640970