All primitives using Expressions

This commit is contained in:
LarsBrubaker 2021-06-05 11:12:26 -07:00
parent f0a71c1a29
commit 082a8cdcc7
20 changed files with 280 additions and 208 deletions

View file

@ -153,10 +153,12 @@ namespace MatterHackers.MatterControl.DesignTools
var graphics2D = _histogramDisplayCache.NewGraphics2D();
graphics2D.Clear(Color.Transparent);
_histogramDisplayCache.CopyFrom(_histogramRawCache);
graphics2D.FillRectangle(0, 0, RangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.Red, 100));
graphics2D.FillRectangle(RangeEnd * _histogramDisplayCache.Width, 0, 255, _histogramDisplayCache.Height, new Color(Color.Red, 100));
graphics2D.Line(RangeStart * _histogramDisplayCache.Width, 0, RangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200));
graphics2D.Line(RangeEnd * _histogramDisplayCache.Width, 0, RangeEnd * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200));
var rangeStart = RangeStart.Value(this);
var rangeEnd = RangeEnd.Value(this);
graphics2D.FillRectangle(0, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.Red, 100));
graphics2D.FillRectangle(rangeEnd * _histogramDisplayCache.Width, 0, 255, _histogramDisplayCache.Height, new Color(Color.Red, 100));
graphics2D.Line(rangeStart * _histogramDisplayCache.Width, 0, rangeStart * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200));
graphics2D.Line(rangeEnd * _histogramDisplayCache.Width, 0, rangeEnd * _histogramDisplayCache.Width, _histogramDisplayCache.Height, new Color(Color.LightGray, 200));
}
}
@ -164,10 +166,10 @@ namespace MatterHackers.MatterControl.DesignTools
private ImageBuffer Image => this.Descendants<ImageObject3D>().FirstOrDefault()?.Image;
[Range(0, 1, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public double RangeStart { get; set; } = .1;
public DoubleOrExpression RangeStart { get; set; } = .1;
[Range(0, 1, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public double RangeEnd { get; set; } = 1;
public DoubleOrExpression RangeEnd { get; set; } = 1;
public IVertexSource VertexSource { get; set; } = new VertexStorage();
@ -178,19 +180,19 @@ namespace MatterHackers.MatterControl.DesignTools
switch (FeatureDetector)
{
case ThresholdFunctions.Silhouette:
return new SilhouetteThresholdFunction(RangeStart, RangeEnd);
return new SilhouetteThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this));
case ThresholdFunctions.Intensity:
return new MapOnMaxIntensity(RangeStart, RangeEnd);
return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this));
case ThresholdFunctions.Alpha:
return new AlphaThresholdFunction(RangeStart, RangeEnd);
return new AlphaThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this));
case ThresholdFunctions.Hue:
return new HueThresholdFunction(RangeStart, RangeEnd);
return new HueThresholdFunction(RangeStart.Value(this), RangeEnd.Value(this));
}
return new MapOnMaxIntensity(RangeStart, RangeEnd);
return new MapOnMaxIntensity(RangeStart.Value(this), RangeEnd.Value(this));
}
}
@ -294,26 +296,28 @@ namespace MatterHackers.MatterControl.DesignTools
bool propertyUpdated = false;
var minSeparation = .01;
if (RangeStart < 0
|| RangeStart > 1
|| RangeEnd < 0
|| RangeEnd > 1
|| RangeStart > RangeEnd - minSeparation)
var rangeStart = RangeStart.Value(this);
var rangeEnd = RangeEnd.Value(this);
if (rangeStart < 0
|| rangeStart > 1
|| rangeEnd < 0
|| rangeEnd > 1
|| rangeStart > rangeEnd - minSeparation)
{
RangeStart = Math.Max(0, Math.Min(1 - minSeparation, RangeStart));
RangeEnd = Math.Max(0, Math.Min(1, RangeEnd));
if (RangeStart > RangeEnd - minSeparation)
rangeStart = Math.Max(0, Math.Min(1 - minSeparation, rangeStart));
rangeEnd = Math.Max(0, Math.Min(1, rangeEnd));
if (rangeStart > rangeEnd - minSeparation)
{
// values are overlapped or too close together
if (RangeEnd < 1 - minSeparation)
if (rangeEnd < 1 - minSeparation)
{
// move the end up whenever possible
RangeEnd = RangeStart + minSeparation;
rangeEnd = rangeStart + minSeparation;
}
else
{
// move the end to the end and the start up
RangeEnd = 1;
rangeEnd = 1;
RangeStart = 1 - minSeparation;
}
}

View file

@ -58,7 +58,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
[Description("The amount to expand the path lines.")]
public double Inflate { get; set; }
public DoubleOrExpression Inflate { get; set; }
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ExpandStyles Style { get; set; } = ExpandStyles.Sharp;
@ -113,7 +113,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
return;
}
VertexSource = path.VertexSource.Offset(Inflate, GetJoinType(Style));
VertexSource = path.VertexSource.Offset(Inflate.Value(this), GetJoinType(Style));
}
internal static JoinType GetJoinType(ExpandStyles style)

View file

@ -50,21 +50,21 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
, IPropertyGridModifier
#endif
{
public double Height { get; set; } = 5;
public DoubleOrExpression Height { get; set; } = 5;
#if DEBUG
[Description("Bevel the top of the extrusion")]
public bool BevelTop { get; set; } = false;
[Description("The amount to inset the bevel")]
public double BevelInset { get; set; } = 2;
public DoubleOrExpression BevelInset { get; set; } = 2;
/// <summary>
[Description("The height the bevel will start")]
/// </summary>
public double BevelStart { get; set; } = 4;
public DoubleOrExpression BevelStart { get; set; } = 4;
public int BevelSteps { get; set; } = 1;
public IntOrExpression BevelSteps { get; set; } = 1;
#endif
public override bool CanFlatten => true;
@ -141,14 +141,12 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
bool valuesChanged = false;
var height = Height.Value(this);
#if DEBUG
if (BevelTop)
{
BevelSteps = agg_basics.Clamp(BevelSteps, 1, 32, ref valuesChanged);
BevelStart = agg_basics.Clamp(BevelStart, 0, Height, ref valuesChanged);
var aabb = this.GetAxisAlignedBoundingBox();
BevelInset = agg_basics.Clamp(BevelInset, 0, Math.Min(aabb.XSize /2, aabb.YSize / 2), ref valuesChanged);
}
var bevelSteps = BevelSteps.ClampIfNotCalculated(this, 1, 32, ref valuesChanged);
var bevelStart = BevelStart.ClampIfNotCalculated(this, 0, height, ref valuesChanged);
var aabb = this.GetAxisAlignedBoundingBox();
var bevelInset = BevelInset.ClampIfNotCalculated(this, 0, Math.Min(aabb.XSize /2, aabb.YSize / 2), ref valuesChanged);
#endif
var rebuildLock = RebuildLock();
@ -164,18 +162,18 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
if (BevelTop)
{
bevel = new List<(double height, double inset)>();
for (int i = 0; i < BevelSteps; i++)
for (int i = 0; i < bevelSteps; i++)
{
var heightRatio = i / (double)BevelSteps;
var height = heightRatio * (Height - BevelStart) + BevelStart;
var insetRatio = (i + 1) / (double)BevelSteps;
var inset = Easing.Sinusoidal.In(insetRatio) * -BevelInset;
var heightRatio = i / (double)bevelSteps;
height = heightRatio * (height - bevelStart) + bevelStart;
var insetRatio = (i + 1) / (double)bevelSteps;
var inset = Easing.Sinusoidal.In(insetRatio) * -bevelInset;
bevel.Add((height, inset));
}
}
#endif
Mesh = VertexSourceToMesh.Extrude(this.VertexSource, Height, bevel);
Mesh = VertexSourceToMesh.Extrude(this.VertexSource, height, bevel);
if (Mesh.Vertices.Count == 0)
{
Mesh = null;

View file

@ -53,9 +53,9 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
}
[Description("The with of the outline.")]
public double OutlineWidth { get; set; } = 3;
public DoubleOrExpression OutlineWidth { get; set; } = 3;
public double Ratio { get; set; } = .5;
public DoubleOrExpression Ratio { get; set; } = .5;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
public ExpandStyles InnerStyle { get; set; } = ExpandStyles.Sharp;
@ -94,9 +94,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
this.DebugDepth("Rebuild");
bool valuesChanged = false;
if (OutlineWidth < .01 || OutlineWidth > 1000)
var outlineWidth = OutlineWidth.Value(this);
if (outlineWidth < .01 || outlineWidth > 1000)
{
OutlineWidth = Math.Min(1000, Math.Max(.01, OutlineWidth));
OutlineWidth = Math.Min(1000, Math.Max(.01, outlineWidth));
valuesChanged = true;
}
@ -130,14 +131,17 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
var offseter = new ClipperOffset();
var outlineWidth = OutlineWidth.Value(this);
var ratio = Ratio.Value(this);
offseter.AddPaths(aPolys, InflatePathObject3D.GetJoinType(OuterStyle), EndType.etClosedPolygon);
var outerLoops = new List<List<IntPoint>>();
offseter.Execute(ref outerLoops, OutlineWidth * Ratio * 1000);
offseter.Execute(ref outerLoops, outlineWidth * ratio * 1000);
Clipper.CleanPolygons(outerLoops);
offseter.AddPaths(aPolys, InflatePathObject3D.GetJoinType(InnerStyle), EndType.etClosedPolygon);
var innerLoops = new List<List<IntPoint>>();
offseter.Execute(ref innerLoops, -OutlineWidth * (1 - Ratio) * 1000);
offseter.Execute(ref innerLoops, -outlineWidth * (1 - ratio) * 1000);
Clipper.CleanPolygons(innerLoops);
var allLoops = outerLoops;

View file

@ -49,15 +49,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
public class RevolveObject3D : Object3D, ISelectedEditorDraw
{
[MaxDecimalPlaces(2)]
public double AxisPosition { get; set; } = 0;
public DoubleOrExpression AxisPosition { get; set; } = 0;
[MaxDecimalPlaces(2)]
public double StartingAngle { get; set; } = 0;
public DoubleOrExpression StartingAngle { get; set; } = 0;
[MaxDecimalPlaces(2)]
public double EndingAngle { get; set; } = 45;
public DoubleOrExpression EndingAngle { get; set; } = 45;
public int Sides { get; set; } = 30;
public IntOrExpression Sides { get; set; } = 30;
public override bool CanFlatten => true;
@ -136,7 +136,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
var aabb = this.GetAxisAlignedBoundingBox();
var vertexSource = this.VertexSource.Transform(Matrix);
var bounds = vertexSource.GetBounds();
var lineX = bounds.Left + AxisPosition;
var lineX = bounds.Left + AxisPosition.Value(this);
var start = new Vector3(lineX, aabb.MinXYZ.Y, aabb.MinXYZ.Z);
var end = new Vector3(lineX, aabb.MaxXYZ.Y, aabb.MinXYZ.Z);
@ -151,16 +151,18 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
this.DebugDepth("Rebuild");
bool valuesChanged = false;
StartingAngle = agg_basics.Clamp(StartingAngle, 0, 360 - .01, ref valuesChanged);
EndingAngle = agg_basics.Clamp(EndingAngle, StartingAngle + .01, 360, ref valuesChanged);
var startingAngle = StartingAngle.ClampIfNotCalculated(this, 0, 360 - .01, ref valuesChanged);
var endingAngle = EndingAngle.ClampIfNotCalculated(this, startingAngle + .01, 360, ref valuesChanged);
var sides = Sides.Value(this);
var axisPosition = AxisPosition.Value(this);
if (StartingAngle > 0 || EndingAngle < 360)
if (startingAngle > 0 || endingAngle < 360)
{
Sides = agg_basics.Clamp(Sides, 1, 360, ref valuesChanged);
Sides = agg_basics.Clamp(sides, 1, 360, ref valuesChanged);
}
else
{
Sides = agg_basics.Clamp(Sides, 3, 360, ref valuesChanged);
Sides = agg_basics.Clamp(sides, 3, 360, ref valuesChanged);
}
if (valuesChanged)
@ -177,15 +179,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
{
var vertexSource = this.VertexSource.Transform(Matrix);
var pathBounds = vertexSource.GetBounds();
vertexSource = vertexSource.Translate(-pathBounds.Left - AxisPosition, 0);
vertexSource = vertexSource.Translate(-pathBounds.Left - axisPosition, 0);
Mesh mesh = VertexSourceToMesh.Revolve(vertexSource,
Sides,
MathHelper.DegreesToRadians(360 - EndingAngle),
MathHelper.DegreesToRadians(360 - StartingAngle),
sides,
MathHelper.DegreesToRadians(360 - endingAngle),
MathHelper.DegreesToRadians(360 - startingAngle),
false);
// take the axis offset out
mesh.Transform(Matrix4X4.CreateTranslation(pathBounds.Left + AxisPosition, 0, 0));
mesh.Transform(Matrix4X4.CreateTranslation(pathBounds.Left + axisPosition, 0, 0));
// move back to object space
mesh.Transform(this.Matrix.Inverted);

View file

@ -52,9 +52,9 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
Name = "Smooth Path".Localize();
}
public double SmoothDistance { get; set; } = .3;
public DoubleOrExpression SmoothDistance { get; set; } = .3;
public int Iterations { get; set; } = 3;
public IntOrExpression Iterations { get; set; } = 3;
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
@ -92,7 +92,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
null,
(reporter, cancellationToken) =>
{
DoSmoothing((long)(SmoothDistance * 1000), Iterations);
DoSmoothing((long)(SmoothDistance.Value(this) * 1000), Iterations.Value(this));
// set the mesh to show the path
this.Mesh = this.VertexSource.Extrude(Constants.PathPolygonsHeight);

View file

@ -57,14 +57,12 @@ namespace MatterHackers.MatterControl.DesignTools
return item;
}
public double Diameter { get; set; } = 20;
//[DisplayName("Top")]
//public double TopDiameter { get; set; } = 0;
public DoubleOrExpression Diameter { get; set; } = 20;
[MaxDecimalPlaces(2)]
public double Height { get; set; } = 20;
public DoubleOrExpression Height { get; set; } = 20;
public int Sides { get; set; } = 40;
public IntOrExpression Sides { get; set; } = 40;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -86,16 +84,16 @@ namespace MatterHackers.MatterControl.DesignTools
using (RebuildLock())
{
Sides = agg_basics.Clamp(Sides, 3, 360, ref changed);
var sides = Sides.ClampIfNotCalculated(this, 3, 360, ref changed);
using (new CenterAndHeightMaintainer(this))
{
var path = new VertexStorage();
path.MoveTo(0, 0);
path.LineTo(Diameter / 2, 0);
path.LineTo(0, Height);
path.LineTo(Diameter.Value(this) / 2, 0);
path.LineTo(0, Height.Value(this));
Mesh = VertexSourceToMesh.Revolve(path, Sides);
Mesh = VertexSourceToMesh.Revolve(path, sides);
}
}
@ -105,9 +103,9 @@ namespace MatterHackers.MatterControl.DesignTools
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
double getHeight() => Height;
double getHeight() => Height.Value(this);
void setHeight(double height) => Height = height;
var getDiameters = new List<Func<double>>() { () => Diameter };
var getDiameters = new List<Func<double>>() { () => Diameter.Value(this) };
var setDiameters = new List<Action<double>>() { (diameter) => Diameter = diameter };
object3DControlsLayer.Object3DControls.Add(new ScaleDiameterControl(object3DControlsLayer,
getHeight,

View file

@ -63,7 +63,7 @@ namespace MatterHackers.MatterControl.DesignTools
public DoubleOrExpression Depth { get; set; } = 20;
[MaxDecimalPlaces(2)]
public int Sides { get; set; } = 20;
public IntOrExpression Sides { get; set; } = 20;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -85,15 +85,15 @@ namespace MatterHackers.MatterControl.DesignTools
using (RebuildLock())
{
Sides = agg_basics.Clamp(Sides, 3, 180, ref valuesChanged);
var sides = Sides.ClampIfNotCalculated(this, 3, 180, ref valuesChanged);
using (new CenterAndHeightMaintainer(this))
{
var path = new VertexStorage();
path.MoveTo(Width.Value(this) / 2, 0);
for (int i = 1; i < Sides; i++)
for (int i = 1; i < sides; i++)
{
var angle = MathHelper.Tau * i / 2 / (Sides - 1);
var angle = MathHelper.Tau * i / 2 / (sides - 1);
path.LineTo(Math.Cos(angle) * Width.Value(this) / 2, Math.Sin(angle) * Width.Value(this) / 2);
}

View file

@ -70,9 +70,9 @@ namespace MatterHackers.MatterControl.DesignTools
return item;
}
public double Diameter { get; set; } = 20;
public int LongitudeSides { get; set; } = 40;
public int LatitudeSides { get; set; } = 10;
public DoubleOrExpression Diameter { get; set; } = 20;
public IntOrExpression LongitudeSides { get; set; } = 40;
public IntOrExpression LatitudeSides { get; set; } = 10;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -93,33 +93,34 @@ namespace MatterHackers.MatterControl.DesignTools
bool valuesChanged = false;
using (RebuildLock())
{
LatitudeSides = agg_basics.Clamp(LatitudeSides, 3, 180, ref valuesChanged);
LongitudeSides = agg_basics.Clamp(LongitudeSides, 3, 360, ref valuesChanged);
var latitudeSides = LatitudeSides.ClampIfNotCalculated(this, 3, 180, ref valuesChanged);
var longitudeSides = LongitudeSides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
var diameter = Diameter.Value(this);
using (new CenterAndHeightMaintainer(this))
{
if (LongitudeSides != lastLongitudeSides
|| LatitudeSides != lastLatitudeSides
|| Diameter != lastDiameter)
if (longitudeSides != lastLongitudeSides
|| latitudeSides != lastLatitudeSides
|| diameter != lastDiameter)
{
var radius = Diameter / 2;
var angleDelta = MathHelper.Tau / 4 / LatitudeSides;
var radius = diameter / 2;
var angleDelta = MathHelper.Tau / 4 / latitudeSides;
var angle = 0.0;
var path = new VertexStorage();
path.MoveTo(0, 0);
path.LineTo(new Vector2(radius * Math.Cos(angle), radius * Math.Sin(angle)));
for (int i = 0; i < LatitudeSides; i++)
for (int i = 0; i < latitudeSides; i++)
{
angle += angleDelta;
path.LineTo(new Vector2(radius * Math.Cos(angle), radius * Math.Sin(angle)));
}
Mesh = VertexSourceToMesh.Revolve(path, LongitudeSides);
Mesh = VertexSourceToMesh.Revolve(path, longitudeSides);
}
lastDiameter = Diameter;
lastLongitudeSides = LongitudeSides;
lastLatitudeSides = LatitudeSides;
lastDiameter = diameter;
lastLongitudeSides = longitudeSides;
lastLatitudeSides = latitudeSides;
}
}
@ -137,7 +138,7 @@ namespace MatterHackers.MatterControl.DesignTools
object3DControlsLayer.Object3DControls.Add(new ScaleDiameterControl(object3DControlsLayer,
null,
null,
new List<Func<double>>() { () => Diameter },
new List<Func<double>>() { () => Diameter.Value(this) },
new List<Action<double>>() { (diameter) => Diameter = diameter },
0));
object3DControlsLayer.AddControls(ControlTypes.MoveInZ);

View file

@ -71,15 +71,15 @@ namespace MatterHackers.MatterControl.DesignTools
}
[MaxDecimalPlaces(2)]
public double OuterDiameter { get; set; } = 20;
public DoubleOrExpression OuterDiameter { get; set; } = 20;
[MaxDecimalPlaces(2)]
public double InnerDiameter { get; set; } = 15;
public DoubleOrExpression InnerDiameter { get; set; } = 15;
[MaxDecimalPlaces(2)]
public double Height { get; set; } = 5;
public DoubleOrExpression Height { get; set; } = 5;
public int Sides { get; set; } = 40;
public IntOrExpression Sides { get; set; } = 40;
public bool Advanced { get; set; } = false;
@ -88,10 +88,10 @@ namespace MatterHackers.MatterControl.DesignTools
public string EasyModeMessage { get; set; } = "You can switch to Advanced mode to get more ring options.";
[MaxDecimalPlaces(2)]
public double StartingAngle { get; set; } = 0;
public DoubleOrExpression StartingAngle { get; set; } = 0;
[MaxDecimalPlaces(2)]
public double EndingAngle { get; set; } = 360;
public DoubleOrExpression EndingAngle { get; set; } = 360;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -112,33 +112,33 @@ namespace MatterHackers.MatterControl.DesignTools
bool valuesChanged = false;
using (RebuildLock())
{
InnerDiameter = agg_basics.Clamp(InnerDiameter, 0, OuterDiameter - .1, ref valuesChanged);
Sides = agg_basics.Clamp(Sides, 3, 360, ref valuesChanged);
StartingAngle = agg_basics.Clamp(StartingAngle, 0, 360 - .01, ref valuesChanged);
EndingAngle = agg_basics.Clamp(EndingAngle, StartingAngle + .01, 360, ref valuesChanged);
var outerDiameter = OuterDiameter.Value(this);
var innerDiameter = InnerDiameter.ClampIfNotCalculated(this, 0, outerDiameter - .1, ref valuesChanged);
var sides = Sides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
var startingAngle = StartingAngle.ClampIfNotCalculated(this, 0, 360 - .01, ref valuesChanged);
var endingAngle = EndingAngle.ClampIfNotCalculated(this, startingAngle + .01, 360, ref valuesChanged);
var height = Height.Value(this);
using (new CenterAndHeightMaintainer(this))
{
var startingAngle = StartingAngle;
var endingAngle = EndingAngle;
if (!Advanced)
{
startingAngle = 0;
endingAngle = 360;
}
var innerDiameter = Math.Min(OuterDiameter - .1, InnerDiameter);
innerDiameter = Math.Min(outerDiameter - .1, innerDiameter);
var path = new VertexStorage();
path.MoveTo(OuterDiameter / 2, -Height / 2);
path.LineTo(OuterDiameter / 2, Height / 2);
path.LineTo(innerDiameter / 2, Height / 2);
path.LineTo(innerDiameter / 2, -Height / 2);
path.LineTo(OuterDiameter / 2, -Height / 2);
path.MoveTo(outerDiameter / 2, -height / 2);
path.LineTo(outerDiameter / 2, height / 2);
path.LineTo(innerDiameter / 2, height / 2);
path.LineTo(innerDiameter / 2, -height / 2);
path.LineTo(outerDiameter / 2, -height / 2);
var startAngle = MathHelper.Range0ToTau(MathHelper.DegreesToRadians(startingAngle));
var endAngle = MathHelper.Range0ToTau(MathHelper.DegreesToRadians(endingAngle));
Mesh = VertexSourceToMesh.Revolve(path, Sides, startAngle, endAngle);
Mesh = VertexSourceToMesh.Revolve(path, sides, startAngle, endAngle);
}
}
@ -160,9 +160,9 @@ namespace MatterHackers.MatterControl.DesignTools
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
double getHeight() => Height;
double getHeight() => Height.Value(this);
void setHeight(double height) => Height = height;
var getDiameters = new List<Func<double>>() { () => OuterDiameter, () => InnerDiameter };
var getDiameters = new List<Func<double>>() { () => OuterDiameter.Value(this), () => InnerDiameter.Value(this) };
var setDiameters = new List<Action<double>>() { (diameter) => OuterDiameter = diameter, (diameter) => InnerDiameter = diameter };
object3DControlsLayer.Object3DControls.Add(new ScaleDiameterControl(object3DControlsLayer,
getHeight,

View file

@ -76,9 +76,9 @@ namespace MatterHackers.MatterControl.DesignTools
}
[MaxDecimalPlaces(2)]
public double Diameter { get; set; } = 20;
public DoubleOrExpression Diameter { get; set; } = 20;
public int Sides { get; set; } = 40;
public IntOrExpression Sides { get; set; } = 40;
public bool Advanced { get; set; } = false;
@ -87,12 +87,12 @@ namespace MatterHackers.MatterControl.DesignTools
public string EasyModeMessage { get; set; } = "You can switch to Advanced mode to get more sphere options.";
[MaxDecimalPlaces(2)]
public double StartingAngle { get; set; } = 0;
public DoubleOrExpression StartingAngle { get; set; } = 0;
[MaxDecimalPlaces(2)]
public double EndingAngle { get; set; } = 360;
public DoubleOrExpression EndingAngle { get; set; } = 360;
public int LatitudeSides { get; set; } = 30;
public IntOrExpression LatitudeSides { get; set; } = 30;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -113,37 +113,35 @@ namespace MatterHackers.MatterControl.DesignTools
bool valuesChanged = false;
using (RebuildLock())
{
Sides = agg_basics.Clamp(Sides, 3, 360, ref valuesChanged);
LatitudeSides = agg_basics.Clamp(LatitudeSides, 3, 360, ref valuesChanged);
StartingAngle = agg_basics.Clamp(StartingAngle, 0, 360 - .01, ref valuesChanged);
EndingAngle = agg_basics.Clamp(EndingAngle, StartingAngle + .01, 360, ref valuesChanged);
var sides = Sides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
var latitudeSides = LatitudeSides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
var startingAngle = StartingAngle.ClampIfNotCalculated(this, 0, 360 - .01, ref valuesChanged);
var endingAngle = EndingAngle.ClampIfNotCalculated(this, startingAngle + .01, 360, ref valuesChanged);
var diameter = Diameter.Value(this);
using (new CenterAndHeightMaintainer(this))
{
if (Sides != lastSides
|| LatitudeSides != lastLatitudeSides
|| StartingAngle != lastStartingAngle
|| EndingAngle != lastEndingAngle
|| Diameter != lastDiameter)
if (sides != lastSides
|| latitudeSides != lastLatitudeSides
|| startingAngle != lastStartingAngle
|| endingAngle != lastEndingAngle
|| diameter != lastDiameter)
{
var startingAngle = StartingAngle;
var endingAngle = EndingAngle;
var latitudeSides = LatitudeSides;
if (!Advanced)
{
startingAngle = 0;
endingAngle = 360;
latitudeSides = Sides;
latitudeSides = sides;
}
Mesh = CreateSphere(Diameter, Sides, latitudeSides, startingAngle, endingAngle);
Mesh = CreateSphere(diameter, sides, latitudeSides, startingAngle, endingAngle);
}
lastDiameter = Diameter;
lastEndingAngle = EndingAngle;
lastStartingAngle = StartingAngle;
lastSides = Sides;
lastLatitudeSides = LatitudeSides;
lastDiameter = diameter;
lastEndingAngle = endingAngle;
lastStartingAngle = startingAngle;
lastSides = sides;
lastLatitudeSides = latitudeSides;
}
}
@ -191,7 +189,7 @@ namespace MatterHackers.MatterControl.DesignTools
object3DControlsLayer.Object3DControls.Add(new ScaleDiameterControl(object3DControlsLayer,
null,
null,
new List<Func<double>>() { () => Diameter },
new List<Func<double>>() { () => Diameter.Value(this) },
new List<Action<double>>() { (diameter) => Diameter = diameter },
0,
ObjectSpace.Placement.Center));

View file

@ -27,6 +27,7 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
@ -64,12 +65,12 @@ namespace MatterHackers.MatterControl.DesignTools
}
[DisplayName("Text")]
public string NameToWrite { get; set; } = "Text";
public StringOrExpression NameToWrite { get; set; } = "Text";
public double PointSize { get; set; } = 24;
public DoubleOrExpression PointSize { get; set; } = 24;
[MaxDecimalPlaces(2)]
public double Height { get; set; } = 5;
public DoubleOrExpression Height { get; set; } = 5;
[Sortable]
[JsonConverter(typeof(StringEnumConverter))]
@ -83,10 +84,11 @@ namespace MatterHackers.MatterControl.DesignTools
var newContainer = new GroupObject3D();
newContainer.CopyProperties(this, Object3DPropertyFlags.All);
int index = 0;
var nameToWrite = NameToWrite.Value(this);
foreach (var child in this.Children)
{
var clone = child.Clone();
var newName = index < NameToWrite.Length ? NameToWrite[index++].ToString() : "Letter".Localize();
var newName = index < nameToWrite.Length ? nameToWrite[index++].ToString() : "Letter".Localize();
clone.Name = MapIfSymbol(newName);
newContainer.Children.Add(clone);
}
@ -139,9 +141,12 @@ namespace MatterHackers.MatterControl.DesignTools
{
using (new CenterAndHeightMaintainer(this))
{
if (string.IsNullOrWhiteSpace(NameToWrite))
bool valuesChanged = false;
var height = Height.ClampIfNotCalculated(this, .01, 1000000, ref valuesChanged);
var nameToWrite = NameToWrite.Value(this);
if (string.IsNullOrWhiteSpace(nameToWrite))
{
Mesh = PlatonicSolids.CreateCube(20, 10, Height);
Mesh = PlatonicSolids.CreateCube(20, 10, height);
}
else
{
@ -153,9 +158,10 @@ namespace MatterHackers.MatterControl.DesignTools
var offest = 0.0;
double pointsToMm = 0.352778;
foreach (var letter in this.NameToWrite.ToCharArray())
foreach (var letter in nameToWrite.ToCharArray())
{
var letterPrinter = new TypeFacePrinter(letter.ToString(), new StyledTypeFace(ApplicationController.GetTypeFace(this.Font), this.PointSize))
var style = new StyledTypeFace(ApplicationController.GetTypeFace(this.Font), PointSize.Value(this));
var letterPrinter = new TypeFacePrinter(letter.ToString(), style)
{
ResolutionScale = 10
};
@ -163,7 +169,7 @@ namespace MatterHackers.MatterControl.DesignTools
list.Add(new Object3D()
{
Mesh = VertexSourceToMesh.Extrude(scaledLetterPrinter, this.Height),
Mesh = VertexSourceToMesh.Extrude(scaledLetterPrinter, this.Height.Value(this)),
Matrix = Matrix4X4.CreateTranslation(offest, 0, 0),
Name = letter.ToString()
});

View file

@ -59,12 +59,12 @@ namespace MatterHackers.MatterControl.DesignTools
}
[MaxDecimalPlaces(2)]
public double OuterDiameter { get; set; } = 20;
public DoubleOrExpression OuterDiameter { get; set; } = 20;
[MaxDecimalPlaces(2)]
public double InnerDiameter { get; set; } = 10;
public DoubleOrExpression InnerDiameter { get; set; } = 10;
public int Sides { get; set; } = 40;
public IntOrExpression Sides { get; set; } = 40;
public bool Advanced { get; set; } = false;
@ -73,15 +73,15 @@ namespace MatterHackers.MatterControl.DesignTools
public string EasyModeMessage { get; set; } = "You can switch to Advanced mode to get more torus options.";
[MaxDecimalPlaces(2)]
public double StartingAngle { get; set; } = 0;
public DoubleOrExpression StartingAngle { get; set; } = 0;
[MaxDecimalPlaces(2)]
public double EndingAngle { get; set; } = 360;
public DoubleOrExpression EndingAngle { get; set; } = 360;
public int RingSides { get; set; } = 15;
public IntOrExpression RingSides { get; set; } = 15;
[MaxDecimalPlaces(2)]
public double RingPhaseAngle { get; set; } = 0;
public DoubleOrExpression RingPhaseAngle { get; set; } = 0;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -102,30 +102,28 @@ namespace MatterHackers.MatterControl.DesignTools
bool valuesChanged = false;
using (RebuildLock())
{
InnerDiameter = agg_basics.Clamp(InnerDiameter, 0, OuterDiameter - .1, ref valuesChanged);
Sides = agg_basics.Clamp(Sides, 3, 360, ref valuesChanged);
RingSides = agg_basics.Clamp(RingSides, 3, 360, ref valuesChanged);
var outerDiameter = OuterDiameter.Value(this);
var innerDiameter = InnerDiameter.ClampIfNotCalculated(this, 0, outerDiameter - .1, ref valuesChanged);
var sides = Sides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
var ringSides = RingSides.ClampIfNotCalculated(this, 3, 360, ref valuesChanged);
StartingAngle = agg_basics.Clamp(StartingAngle, 0, 360 - .01, ref valuesChanged);
EndingAngle = agg_basics.Clamp(EndingAngle, StartingAngle + .01, 360, ref valuesChanged);
var startingAngle = StartingAngle.ClampIfNotCalculated(this, 0, 360 - .01, ref valuesChanged);
var endingAngle = EndingAngle.ClampIfNotCalculated(this, startingAngle + .01, 360, ref valuesChanged);
var ringSides = RingSides;
var startingAngle = StartingAngle;
var endingAngle = EndingAngle;
var ringPhaseAngle = RingPhaseAngle;
var ringPhaseAngle = RingPhaseAngle.Value(this);
if (!Advanced)
{
ringSides = Math.Max(3, (int)(Sides / 2));
ringSides = Math.Max(3, (int)(sides / 2));
startingAngle = 0;
endingAngle = 360;
ringPhaseAngle = 0;
}
var innerDiameter = Math.Min(OuterDiameter - .1, InnerDiameter);
innerDiameter = Math.Min(outerDiameter - .1, innerDiameter);
using (new CenterAndHeightMaintainer(this))
{
var poleRadius = (OuterDiameter / 2 - innerDiameter / 2) / 2;
var poleRadius = (outerDiameter / 2 - innerDiameter / 2) / 2;
var toroidRadius = innerDiameter / 2 + poleRadius;
var path = new VertexStorage();
var angleDelta = MathHelper.Tau / ringSides;
@ -143,7 +141,7 @@ namespace MatterHackers.MatterControl.DesignTools
var startAngle = MathHelper.Range0ToTau(MathHelper.DegreesToRadians(startingAngle));
var endAngle = MathHelper.Range0ToTau(MathHelper.DegreesToRadians(endingAngle));
Mesh = VertexSourceToMesh.Revolve(path, Sides, startAngle, endAngle);
Mesh = VertexSourceToMesh.Revolve(path, sides, startAngle, endAngle);
}
}
@ -167,7 +165,7 @@ namespace MatterHackers.MatterControl.DesignTools
public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
var getDiameters = new List<Func<double>>() { () => OuterDiameter, () => InnerDiameter };
var getDiameters = new List<Func<double>>() { () => OuterDiameter.Value(this), () => InnerDiameter.Value(this) };
var setDiameters = new List<Action<double>>() { (diameter) => OuterDiameter = diameter, (diameter) => InnerDiameter = diameter };
object3DControlsLayer.Object3DControls.Add(new ScaleDiameterControl(object3DControlsLayer,
null,

View file

@ -69,7 +69,7 @@ namespace MatterHackers.MatterControl.DesignTools
public bool Round { get; set; } = false;
public int RoundSegments { get; set; } = 15;
public IntOrExpression RoundSegments { get; set; } = 15;
public override async void OnInvalidate(InvalidateArgs invalidateType)
{
@ -89,7 +89,7 @@ namespace MatterHackers.MatterControl.DesignTools
this.DebugDepth("Rebuild");
bool valuesChanged = false;
RoundSegments = agg_basics.Clamp(RoundSegments, 3, 360 / 4 - 2, ref valuesChanged);
var roundSegments = RoundSegments.ClampIfNotCalculated(this, 3, 360 / 4 - 2, ref valuesChanged);
if (valuesChanged)
{
@ -107,9 +107,9 @@ namespace MatterHackers.MatterControl.DesignTools
if (Round)
{
var range = 360 / 4.0;
for (int i = 1; i < RoundSegments - 1; i++)
for (int i = 1; i < roundSegments - 1; i++)
{
var angle = range / (RoundSegments - 1) * i;
var angle = range / (roundSegments - 1) * i;
var rad = MathHelper.DegreesToRadians(angle);
path.LineTo(Width.Value(this) - Math.Sin(rad) * Width.Value(this), Height.Value(this) - Math.Cos(rad) * Height.Value(this));
}

View file

@ -94,6 +94,8 @@ namespace MatterHackers.MatterControl.DesignTools
GuiWidget scope = mainContainer;
rows.Clear();
// Create a field editor for each editable property detected via reflection
foreach (var property in GetEditablePropreties(context.item))
{
@ -204,11 +206,14 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
private static SettingsRow CreateSettingsRow(List<SettingsRow> rows, double textRightMargin, EditableProperty property, UIField field, ThemeConfig theme)
private static SettingsRow CreateSettingsRow(EditableProperty property, UIField field, ThemeConfig theme, List<SettingsRow> rows = null)
{
var row = new SettingsRow(property.DisplayName.Localize(), property.Description, field.Content, theme);
rows.Add(row);
row.SetTextRightMargin(rows, textRightMargin);
if (rows != null)
{
rows.Add(row);
row.SetTextRightMargin(rows);
}
return row;
}
@ -289,8 +294,6 @@ namespace MatterHackers.MatterControl.DesignTools
GuiWidget rowContainer = null;
var textRightMargin = 10 * GuiWidget.DeviceScale;
// Get reflected property value once, then test for each case below
var propertyValue = property.Value;
@ -388,7 +391,7 @@ namespace MatterHackers.MatterControl.DesignTools
field.Content.Descendants<InternalNumberEdit>().First().MaxDecimalsPlaces = decimalPlaces.Number;
}
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
}
else if (propertyValue is Color color)
@ -402,7 +405,7 @@ namespace MatterHackers.MatterControl.DesignTools
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else if (propertyValue is Vector2 vector2)
{
@ -476,7 +479,7 @@ namespace MatterHackers.MatterControl.DesignTools
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else if (propertyValue is DirectionAxis directionAxis)
{
@ -558,7 +561,7 @@ namespace MatterHackers.MatterControl.DesignTools
return childrenSelector;
});
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else // show the subtract editor for boolean subtract and subtract and replace
{
@ -660,7 +663,7 @@ namespace MatterHackers.MatterControl.DesignTools
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
}
else if (propertyValue is bool boolValue)
@ -673,7 +676,7 @@ namespace MatterHackers.MatterControl.DesignTools
RegisterValueChanged(field,
(valueString) => { return valueString == "1"; },
(value) => { return ((bool)value) ? "1" : "0"; });
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else if (propertyValue is DoubleOrExpression doubleExpresion)
{
@ -688,7 +691,7 @@ namespace MatterHackers.MatterControl.DesignTools
{
return ((DoubleOrExpression)value).Expression;
});
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme, rows);
void RefreshField(object s, InvalidateArgs e)
{
@ -724,11 +727,7 @@ namespace MatterHackers.MatterControl.DesignTools
{
return ((IntOrExpression)value).Expression;
});
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
var spacer = rowContainer.Children.OfType<HorizontalSpacer>().FirstOrDefault();
spacer.HAnchor = HAnchor.Absolute;
var label = rowContainer.Children<TextWidget>().First();
spacer.Width = Math.Max(0, 100 * GuiWidget.DeviceScale - label.Width);
rowContainer = CreateSettingsRow(property, field, theme, rows);
void RefreshField(object s, InvalidateArgs e)
{
@ -827,13 +826,7 @@ namespace MatterHackers.MatterControl.DesignTools
field.ClearUndoHistory();
field.Content.HAnchor = HAnchor.Stretch;
RegisterValueChanged(field, (valueString) => valueString);
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
var label = rowContainer.Children<TextWidget>().First();
var spacer = rowContainer.Children.OfType<HorizontalSpacer>().FirstOrDefault();
spacer.HAnchor = HAnchor.Absolute;
spacer.Width = Math.Max(0, 100 * GuiWidget.DeviceScale - label.Width);
rowContainer = CreateSettingsRow(property, field, theme, rows);
}
}
}
@ -851,7 +844,7 @@ namespace MatterHackers.MatterControl.DesignTools
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else if (property.PropertyType.IsEnum)
{
@ -902,7 +895,7 @@ namespace MatterHackers.MatterControl.DesignTools
if (addToSettingsRow)
{
rowContainer = CreateSettingsRow(rows, textRightMargin, property, field, theme);
rowContainer = CreateSettingsRow(property, field, theme);
}
else
{

View file

@ -44,6 +44,8 @@ namespace MatterHackers.MatterControl.DesignTools
public string Expression { get; set; }
public override string ToString() => Expression;
public double Value(IObject3D owner)
{
return SheetObject3D.EvaluateExpression<double>(owner, Expression);

View file

@ -0,0 +1,62 @@
/*
Copyright (c) 2019, 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.ComponentModel;
using MatterHackers.DataConverters3D;
namespace MatterHackers.MatterControl.DesignTools
{
[TypeConverter(typeof(StringOrExpression))]
public class StringOrExpression
{
/// <summary>
/// Is the expression referencing a cell in the table or an equation. If not it is simply a constant
/// </summary>
public bool IsEquation { get => Expression.Length > 0 && Expression[0] == '='; }
public string Expression { get; set; }
public override string ToString() => Expression;
public string Value(IObject3D owner)
{
return SheetObject3D.EvaluateExpression<string>(owner, Expression);
}
public StringOrExpression(string expression)
{
Expression = expression;
}
public static implicit operator StringOrExpression(string expression)
{
return new StringOrExpression(expression);
}
}
}

View file

@ -1015,7 +1015,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var progressBackgroundColor = new Color(theme.AccentMimimalOverlay, 35);
int displayedTaskCount = 0;
// Add new items
foreach (var taskItem in tasks.RunningTasks.Where(t => !displayedTasks.Contains(t)))
{
@ -1037,13 +1036,19 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
};
tasksContainer.AddChild(runningTaskPanel);
displayedTaskCount++;
}
if (displayedTaskCount == 0)
if (!string.IsNullOrEmpty(ApplicationController.Instance.UiHint))
{
statusMessage.Text = ApplicationController.Instance.UiHint;
statusMessage.Visible = true;
var parent = statusMessage.Parent;
if (parent.Children.IndexOf(statusMessage) != parent.Children.Count - 1)
{
parent.RemoveChild(statusMessage);
statusMessage.ClearRemovedFlag();
parent.AddChild(statusMessage);
}
}
else
{

View file

@ -117,18 +117,19 @@ namespace MatterHackers.MatterControl.CustomWidgets
this.PerformLayout();
}
public SettingsRow SetTextRightMargin(List<SettingsRow> rows, double spacing)
public SettingsRow SetTextRightMargin(List<SettingsRow> rows)
{
var spacing = 11 * GuiWidget.DeviceScale;
var maxTextWidth = 0.0;
foreach (var row in rows)
{
maxTextWidth = Math.Max(maxTextWidth, row.textLabel.Width);
}
spacer.HAnchor = HAnchor.Absolute;
var newWidth = spacing + maxTextWidth;
foreach (var row in rows)
{
row.spacer.HAnchor = HAnchor.Absolute;
row.spacer.Width = Math.Max(0, newWidth - row.textLabel.Width);
}

@ -1 +1 @@
Subproject commit 9bdf0fa6f30c8245ceeca65967da1665a355ea46
Subproject commit c71a0368af2e32a2091d05c03d4e9385f3858e27