From 197babd9dc80498e5ee0f7e8052e7941496e1008 Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Mon, 7 Nov 2022 08:34:52 -0800 Subject: [PATCH 1/6] improving linear extrude bevel curvature --- .../Operations/Path/LinearExtrudeObject3D.cs | 28 +++++++++++-------- Submodules/agg-sharp | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs index d318e58b6..a0e84ae49 100644 --- a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs @@ -30,21 +30,17 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.Localizations; using MatterHackers.MatterControl.PartPreviewWindow; -using MatterHackers.Plugins.EditorTools; using MatterHackers.PolygonMesh; using MatterHackers.PolygonMesh.Processors; using MatterHackers.VectorMath; -using Newtonsoft.Json; namespace MatterHackers.MatterControl.DesignTools.Operations { @@ -62,6 +58,9 @@ namespace MatterHackers.MatterControl.DesignTools.Operations [Description("Bevel the top of the extrusion")] public bool BevelTop { get; set; } = false; + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public ExpandStyles Style { get; set; } = ExpandStyles.Sharp; + [Description("The amount to inset the bevel")] public DoubleOrExpression BevelInset { get; set; } = 2; @@ -157,20 +156,26 @@ namespace MatterHackers.MatterControl.DesignTools.Operations #if DEBUG if (BevelTop) { + var curve4 = new Curve4Increment(); + curve4.Init(0, 0, + 0, (height - bevelStart)/2, + bevelInset / 2, height - bevelStart, + bevelInset, height - bevelStart, + bevelSteps); bevel = new List<(double height, double inset)>(); - for (int i = 0; i < bevelSteps; i++) + curve4.Vertex(out double x, out double y); + for (int i = 1; i < bevelSteps; i++) { - var heightRatio = i / (double)bevelSteps; - var height2 = heightRatio * (height - bevelStart) + bevelStart; - var insetRatio = (i + 1) / (double)bevelSteps; - var inset = Easing.Sinusoidal.In(insetRatio) * -bevelInset; - bevel.Add((height2, inset)); + curve4.Vertex(out x, out y); + bevel.Add((bevelStart + y, -x)); } + + //bevel.Add((height, -bevelInset)); } #endif if (this.GetVertexSource() != null) { - Mesh = VertexSourceToMesh.Extrude(this.GetVertexSource(), height, bevel); + Mesh = VertexSourceToMesh.Extrude(this.GetVertexSource(), height, bevel, InflatePathObject3D.GetJoinType(Style)); if (Mesh.Vertices.Count == 0) { Mesh = null; @@ -199,6 +204,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations change.SetRowVisible(nameof(BevelStart), () => BevelTop); change.SetRowVisible(nameof(BevelInset), () => BevelTop); change.SetRowVisible(nameof(BevelSteps), () => BevelTop); + change.SetRowVisible(nameof(Style), () => BevelTop); } #endif } diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 94727927b..0d0d08f1c 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 94727927b558c1a93025d97c0e33f5eff7d4d289 +Subproject commit 0d0d08f1cb4c56b977354b246fd002deb98ba8f7 From 997f9dfcabef90ca8421dfaf665c50830368dd9f Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Thu, 10 Nov 2022 12:43:06 -0800 Subject: [PATCH 2/6] Change to radius --- .../Operations/Path/LinearExtrudeObject3D.cs | 53 ++++++++----------- StaticData/Translations/Master.txt | 3 ++ 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs index a0e84ae49..7f4bbc266 100644 --- a/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Path/LinearExtrudeObject3D.cs @@ -61,18 +61,14 @@ namespace MatterHackers.MatterControl.DesignTools.Operations [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] public ExpandStyles Style { get; set; } = ExpandStyles.Sharp; - [Description("The amount to inset the bevel")] - public DoubleOrExpression BevelInset { get; set; } = 2; + [Slider(0, 20, Easing.EaseType.Quadratic, snapDistance: .1)] + public DoubleOrExpression Radius { get; set; } = 3; - /// - [Description("The height the bevel will start")] - /// - public DoubleOrExpression BevelStart { get; set; } = 4; - - public IntOrExpression BevelSteps { get; set; } = 1; + [Slider(1, 20, Easing.EaseType.Quadratic, snapDistance: 1)] + public IntOrExpression Segments { get; set; } = 9; #endif - public override bool CanApply => true; + public override bool CanApply => true; public override IVertexSource GetVertexSource() { @@ -130,6 +126,11 @@ namespace MatterHackers.MatterControl.DesignTools.Operations } } + private (double x, double y) GetOffset(double radius, double xRatio, double yRatio) + { + return (radius * Math.Cos(xRatio * MathHelper.Tau / 4), radius * Math.Sin(yRatio * MathHelper.Tau / 4)); + } + public override Task Rebuild() { this.DebugDepth("Rebuild"); @@ -139,10 +140,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations var height = Height.Value(this); #if DEBUG - 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); + var segments = Segments.ClampIfNotCalculated(this, 1, 32, ref valuesChanged); + var aabb = this.GetAxisAlignedBoundingBox(); + var radius = Radius.ClampIfNotCalculated(this, 0, Math.Min(aabb.XSize, Math.Min(aabb.YSize, aabb.ZSize)) / 2, ref valuesChanged); + var bevelStart = height - radius; #endif // now create a long running task to do the extrusion @@ -156,24 +157,15 @@ namespace MatterHackers.MatterControl.DesignTools.Operations #if DEBUG if (BevelTop) { - var curve4 = new Curve4Increment(); - curve4.Init(0, 0, - 0, (height - bevelStart)/2, - bevelInset / 2, height - bevelStart, - bevelInset, height - bevelStart, - bevelSteps); - bevel = new List<(double height, double inset)>(); - curve4.Vertex(out double x, out double y); - for (int i = 1; i < bevelSteps; i++) + bevel = new List<(double, double)>(); + for (int i = 0; i < segments; i++) { - curve4.Vertex(out x, out y); - bevel.Add((bevelStart + y, -x)); + (double x, double y) = GetOffset(radius, (i + 1) / (double)segments, i / (double)segments); + bevel.Add((bevelStart + y, -radius+x)); } - - //bevel.Add((height, -bevelInset)); - } + } #endif - if (this.GetVertexSource() != null) + if (this.GetVertexSource() != null) { Mesh = VertexSourceToMesh.Extrude(this.GetVertexSource(), height, bevel, InflatePathObject3D.GetJoinType(Style)); if (Mesh.Vertices.Count == 0) @@ -201,9 +193,8 @@ namespace MatterHackers.MatterControl.DesignTools.Operations #if DEBUG public void UpdateControls(PublicPropertyChange change) { - change.SetRowVisible(nameof(BevelStart), () => BevelTop); - change.SetRowVisible(nameof(BevelInset), () => BevelTop); - change.SetRowVisible(nameof(BevelSteps), () => BevelTop); + change.SetRowVisible(nameof(Radius), () => BevelTop); + change.SetRowVisible(nameof(Segments), () => BevelTop); change.SetRowVisible(nameof(Style), () => BevelTop); } #endif diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index 5241998e6..70cb79c48 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -4420,6 +4420,9 @@ Translated:seconds English:Sections Translated:Sections +English:Segments +Translated:Segments + English:Select Translated:Select From 17dd2b256f6fac4b0b173f9244208342a82eac78 Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Tue, 15 Nov 2022 10:04:13 -0800 Subject: [PATCH 3/6] updated size --- MatterControlLib/DesignTools/Primitives/CubeObject3D.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs b/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs index 97f03c54b..90ce5877b 100644 --- a/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/CubeObject3D.cs @@ -67,7 +67,7 @@ namespace MatterHackers.MatterControl.DesignTools public bool Round { get; set; } - [Slider(0, 30, Easing.EaseType.Quadratic, snapDistance: .1)] + [Slider(0, 20, Easing.EaseType.Quadratic, snapDistance: .1)] public DoubleOrExpression Radius { get; set; } = 3; [Slider(1, 20, Easing.EaseType.Quadratic, snapDistance: 1)] From dca3b4edf239f8e555cf17dd1bc2ea2fc8af4138 Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Fri, 25 Nov 2022 10:58:41 -0800 Subject: [PATCH 4/6] Adding QRCode objec Moving classes to new files Adding sheet data calculation tests --- .../ApplicationView/SceneOperations.cs | 4 +- .../EditorTools/LithophanePlugin.cs | 6 +- .../DesignTools/LithophaneObject3D.cs | 2 +- .../Primitives/IEditorWidgetModifier.cs | 39 ++ .../DesignTools/Primitives/IImageProvider.cs | 38 ++ .../DesignTools/Primitives/ImageObject3D.cs | 11 +- .../DesignTools/Primitives/QrCodeObject3D.cs | 475 ++++++++++++++++++ .../MatterControl/PrimitivesContainer.cs | 4 + MatterControlLib/MatterControlLib.csproj | 2 + StaticData/Translations/Master.txt | 3 + Submodules/MatterSlice | 2 +- Submodules/agg-sharp | 2 +- .../DesignTools/SheetTests.cs | 28 ++ 13 files changed, 598 insertions(+), 18 deletions(-) create mode 100644 MatterControlLib/DesignTools/Primitives/IEditorWidgetModifier.cs create mode 100644 MatterControlLib/DesignTools/Primitives/IImageProvider.cs create mode 100644 MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs diff --git a/MatterControlLib/ApplicationView/SceneOperations.cs b/MatterControlLib/ApplicationView/SceneOperations.cs index 721492179..c7812b78a 100644 --- a/MatterControlLib/ApplicationView/SceneOperations.cs +++ b/MatterControlLib/ApplicationView/SceneOperations.cs @@ -353,7 +353,7 @@ namespace MatterHackers.MatterControl { var scene = sceneContext.Scene; var sceneItem = scene.SelectedItem; - if (sceneItem is IObject3D imageObject) + if (sceneItem is IImageProvider imageObject) { // TODO: make it look like this (and get rid of all the other stuff) // scene.Replace(sceneItem, new ImageToPathObject3D_2(sceneItem.Clone())); @@ -373,7 +373,7 @@ namespace MatterHackers.MatterControl }, Icon = (theme) => StaticData.Instance.LoadIcon("image_to_path.png", 16, 16).SetToColor(theme.TextColor).SetPreMultiply(), HelpTextGetter = () => "An image must be selected".Localize().Stars(), - IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null && sceneContext.Scene.SelectedItem is ImageObject3D, + IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null && sceneContext.Scene.SelectedItem is IImageProvider, }; } diff --git a/MatterControlLib/DesignTools/EditorTools/LithophanePlugin.cs b/MatterControlLib/DesignTools/EditorTools/LithophanePlugin.cs index 52afd8793..36f15bd20 100644 --- a/MatterControlLib/DesignTools/EditorTools/LithophanePlugin.cs +++ b/MatterControlLib/DesignTools/EditorTools/LithophanePlugin.cs @@ -80,14 +80,14 @@ namespace MatterHackers.MatterControl.Plugins.Lithophane { var scene = sceneContext.Scene; var sceneItem = scene.SelectedItem; - if (sceneItem is IObject3D imageObject) + if (sceneItem is IImageProvider imageObject) { WrapWith(sceneItem, new LithophaneObject3D(), scene); } }, - IsEnabled = (sceneContext) => sceneContext?.Scene?.SelectedItem is ImageObject3D, + IsEnabled = (sceneContext) => sceneContext?.Scene?.SelectedItem is IImageProvider, HelpTextGetter = () => "An image must be selected".Localize().Stars(), - ShowInModifyMenu = (sceneContext) => sceneContext?.Scene?.SelectedItem is ImageObject3D, + ShowInModifyMenu = (sceneContext) => sceneContext?.Scene?.SelectedItem is IImageProvider, Icon = (theme) => StaticData.Instance.LoadIcon("lithophane.png", 16, 16).SetToColor(theme.TextColor) }, "Image"); diff --git a/MatterControlLib/DesignTools/LithophaneObject3D.cs b/MatterControlLib/DesignTools/LithophaneObject3D.cs index 31d6b1571..15638a6f7 100644 --- a/MatterControlLib/DesignTools/LithophaneObject3D.cs +++ b/MatterControlLib/DesignTools/LithophaneObject3D.cs @@ -53,7 +53,7 @@ namespace MatterHackers.MatterControl.Plugins.Lithophane } [JsonIgnore] - public ImageObject3D ImageChild => this.Children.OfType().FirstOrDefault(); + public IImageProvider ImageChild => this.Children.OfType().FirstOrDefault(); [DisplayName("Pixels Per mm"), Range(0.5, 3, ErrorMessage = "Value for {0} must be between {1} and {2}.")] public double PixelsPerMM { get; set; } = 1.5; diff --git a/MatterControlLib/DesignTools/Primitives/IEditorWidgetModifier.cs b/MatterControlLib/DesignTools/Primitives/IEditorWidgetModifier.cs new file mode 100644 index 000000000..88a99fb99 --- /dev/null +++ b/MatterControlLib/DesignTools/Primitives/IEditorWidgetModifier.cs @@ -0,0 +1,39 @@ +/* +Copyright (c) 2022 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.UI; + +namespace MatterHackers.MatterControl.DesignTools +{ + public interface IEditorWidgetModifier + { + void ModifyEditorWidget(GuiWidget widget, ThemeConfig theme, Action requestWidgetUpdate); + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Primitives/IImageProvider.cs b/MatterControlLib/DesignTools/Primitives/IImageProvider.cs new file mode 100644 index 000000000..48ee1e33c --- /dev/null +++ b/MatterControlLib/DesignTools/Primitives/IImageProvider.cs @@ -0,0 +1,38 @@ +/* +Copyright (c) 2022 Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg.Image; + +namespace MatterHackers.MatterControl.DesignTools +{ + public interface IImageProvider + { + ImageBuffer Image { get; } + } +} \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs b/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs index 9665357ba..b96db19cb 100644 --- a/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs @@ -46,17 +46,8 @@ using Newtonsoft.Json; namespace MatterHackers.MatterControl.DesignTools { - public interface IEditorWidgetModifier - { - void ModifyEditorWidget(GuiWidget widget, ThemeConfig theme, Action requestWidgetUpdate); - } - public interface IImageProvider - { - ImageBuffer Image { get; } - } - - [HideMeterialAndColor] + [HideMeterialAndColor] public class ImageObject3D : AssetObject3D, IImageProvider, IObject3DControlsProvider, IEditorWidgetModifier { private const double DefaultSizeMm = 60; diff --git a/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs new file mode 100644 index 000000000..b6842a54e --- /dev/null +++ b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs @@ -0,0 +1,475 @@ +/* +Copyright (c) 2022 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.ComponentModel; +using System.IO; +using System.Threading.Tasks; +using MatterHackers.Agg; +using MatterHackers.Agg.Image; +using MatterHackers.Agg.ImageProcessing; +using MatterHackers.Agg.Platform; +using MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DataStorage; +using MatterHackers.MatterControl.PartPreviewWindow; +using MatterHackers.PolygonMesh; +using Newtonsoft.Json; +using QRCoder; + +namespace MatterHackers.MatterControl.DesignTools +{ + [HideMeterialAndColor] + public class QrCodeObject3D : Object3D, IImageProvider, IObject3DControlsProvider + { + private const double DefaultSizeMm = 60; + + private ImageBuffer _image; + + private bool _invert; + + public QrCodeObject3D() + { + Name = "QR Code".Localize(); + } + + public static async Task Create() + { + var item = new QrCodeObject3D(); + await item.Rebuild(); + return item; + } + + public override bool CanApply => false; + + public StringOrExpression Text { get; set; } = "https://www.matterhackers.com"; + + [DisplayName("")] + [JsonIgnore] + [ImageDisplay(Margin = new int[] { 9, 3, 9, 3 }, MaxXSize = 400, Stretch = true)] + public ImageBuffer Image + { + get + { + if (_image == null) + { + RebuildImage(); + + // send the invalidate on image change + this.CancelAllParentBuilding(); + Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Image)); + Invalidate(InvalidateType.DisplayValues); + } + + return _image; + } + + set + { + } + } + + private void RebuildImage() + { + // set a temp image so we don't have any problems with threading + var image = this.BuildImage(); + + if (image != null) + { + if (this.Invert) + { + image = InvertLightness.DoInvertLightness(image); + } + } + else // bad load + { + image = new ImageBuffer(200, 100); + var graphics2D = image.NewGraphics2D(); + graphics2D.Clear(Color.White); + graphics2D.DrawString("Image Missing".Localize(), image.Width / 2, image.Height / 2, 20, Agg.Font.Justification.Center, Agg.Font.Baseline.BoundsCenter); + } + + // we don't want to invalidate on the mesh change + using (RebuildLock()) + { + base.Mesh = this.InitMesh(image) ?? PlatonicSolids.CreateCube(100, 100, 0.2); + } + + if (_image == null) + { + _image = image; + } + else + { + _image.CopyFrom(image); + } + } + + public bool Invert + { + get => _invert; + set + { + if (_invert != value) + { + _invert = value; + RebuildImage(); + Invalidate(InvalidateType.Image); + } + } + } + + public override async void OnInvalidate(InvalidateArgs invalidateArgs) + { + if ((invalidateArgs.InvalidateType.HasFlag(InvalidateType.Properties) && invalidateArgs.Source == this)) + { + RebuildImage(); + await Rebuild(); + } + else if (SheetObject3D.NeedsRebuild(this, invalidateArgs)) + { + RebuildImage(); + await Rebuild(); + } + else + { + base.OnInvalidate(invalidateArgs); + } + } + + public static string GetFileOrAsset(string file) + { + if (!File.Exists(file)) + { + var path = Path.Combine(ApplicationDataStorage.Instance.LibraryAssetsPath, file); + if (File.Exists(path)) + { + return path; + } + + // can't find a real file + return null; + } + + return file; + } + + + public static bool FilesAreEqual(string first, string second) + { + if (string.IsNullOrEmpty(first) + || string.IsNullOrEmpty(second)) + { + return false; + } + + var diskFirst = GetFileOrAsset(first); + var diskSecond = GetFileOrAsset(second); + if (File.Exists(diskFirst) && File.Exists(diskSecond)) + { + return FilesAreEqual(new FileInfo(diskFirst), new FileInfo(diskSecond)); + } + + return false; + } + + public static bool FilesAreEqual(FileInfo first, FileInfo second) + { + if (first.Length != second.Length) + { + return false; + } + + if (string.Equals(first.FullName, second.FullName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + int readSize = 1 << 16; + int numReads = (int)Math.Ceiling((double)first.Length / readSize); + + using (var firstFs = first.OpenRead()) + { + using (var secondFs = second.OpenRead()) + { + byte[] one = new byte[readSize]; + byte[] two = new byte[readSize]; + + for (int i = 0; i < numReads; i++) + { + firstFs.Read(one, 0, readSize); + secondFs.Read(two, 0, readSize); + + for (int j = 0; j < readSize; j++) + { + if (one[j] != two[j]) + { + return false; + } + } + } + } + } + + return true; + } + public void AddObject3DControls(Object3DControlsLayer object3DControlsLayer) + { + object3DControlsLayer.AddControls(ControlTypes.Standard2D); + } + + private ImageBuffer BuildImage() + { + QRCodeGenerator qrGenerator = new QRCodeGenerator(); + QRCodeData qrCodeData = qrGenerator.CreateQrCode(Text.Value(this), QRCodeGenerator.ECCLevel.Q); + QRCode qrCode = new QRCode(qrCodeData); + System.Drawing.Bitmap qrCodeImage = qrCode.GetGraphic(16); + + var destImage = new ImageBuffer(); + ConvertBitmapToImage(destImage, qrCodeImage); + return destImage; + } + + public bool ConvertBitmapToImage(ImageBuffer destImage, System.Drawing.Bitmap bitmap) + { + if (bitmap != null) + { + switch (bitmap.PixelFormat) + { + case System.Drawing.Imaging.PixelFormat.Format32bppArgb: + { + destImage.Allocate(bitmap.Width, bitmap.Height, bitmap.Width * 4, 32); + if (destImage.GetRecieveBlender() == null) + { + destImage.SetRecieveBlender(new BlenderBGRA()); + } + + System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits( + new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat); + int sourceIndex = 0; + int destIndex = 0; + unsafe + { + byte[] destBuffer = destImage.GetBuffer(out int offset); + byte* pSourceBuffer = (byte*)bitmapData.Scan0; + for (int y = 0; y < destImage.Height; y++) + { + destIndex = destImage.GetBufferOffsetXY(0, destImage.Height - 1 - y); + for (int x = 0; x < destImage.Width; x++) + { +#if true + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; +#else + Color notPreMultiplied = new Color(pSourceBuffer[sourceIndex + 0], pSourceBuffer[sourceIndex + 1], pSourceBuffer[sourceIndex + 2], pSourceBuffer[sourceIndex + 3]); + sourceIndex += 4; + Color preMultiplied = notPreMultiplied.ToColorF().premultiply().ToColor(); + destBuffer[destIndex++] = preMultiplied.blue; + destBuffer[destIndex++] = preMultiplied.green; + destBuffer[destIndex++] = preMultiplied.red; + destBuffer[destIndex++] = preMultiplied.alpha; +#endif + } + } + } + + bitmap.UnlockBits(bitmapData); + + return true; + } + + case System.Drawing.Imaging.PixelFormat.Format24bppRgb: + { + destImage.Allocate(bitmap.Width, bitmap.Height, bitmap.Width * 4, 32); + if (destImage.GetRecieveBlender() == null) + { + destImage.SetRecieveBlender(new BlenderBGRA()); + } + + System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat); + int sourceIndex = 0; + int destIndex = 0; + unsafe + { + byte[] destBuffer = destImage.GetBuffer(out int offset); + byte* pSourceBuffer = (byte*)bitmapData.Scan0; + for (int y = 0; y < destImage.Height; y++) + { + sourceIndex = y * bitmapData.Stride; + destIndex = destImage.GetBufferOffsetXY(0, destImage.Height - 1 - y); + for (int x = 0; x < destImage.Width; x++) + { + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = pSourceBuffer[sourceIndex++]; + destBuffer[destIndex++] = 255; + } + } + } + + bitmap.UnlockBits(bitmapData); + return true; + } + + default: + // let this code fall through and return false + break; + } + } + + return false; + } + + public override Mesh Mesh + { + get + { + if (base.Mesh == null || base.Mesh.FaceTextures.Count <= 0) + { + using (this.RebuildLock()) + { + // TODO: Revise fallback mesh + base.Mesh = this.InitMesh(this.Image) ?? PlatonicSolids.CreateCube(100, 100, 0.2); + } + } + + return base.Mesh; + } + } + + public double ScaleMmPerPixels { get; private set; } + + public override Task Rebuild() + { + InitMesh(this.Image); + + UiThread.RunOnIdle(() => Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Image))); + + return base.Rebuild(); + } + + private Mesh InitMesh(ImageBuffer imageBuffer) + { + if (imageBuffer != null) + { + ScaleMmPerPixels = Math.Min(DefaultSizeMm / imageBuffer.Width, DefaultSizeMm / imageBuffer.Height); + + // Create texture mesh + double width = ScaleMmPerPixels * imageBuffer.Width; + double height = ScaleMmPerPixels * imageBuffer.Height; + + Mesh textureMesh = PlatonicSolids.CreateCube(width, height, 0.2); + textureMesh.PlaceTextureOnFaces(0, imageBuffer); + + return textureMesh; + } + + return null; + } + + public static void ModifyImageObjectEditorWidget(ImageObject3D imageObject, GuiWidget widget, ThemeConfig theme, Action requestWidgetUpdate) + { + widget.Click += (s, e) => + { + if (e.Button == MouseButtons.Left) + { + ShowOpenDialog(imageObject); + } + + if (e.Button == MouseButtons.Right) + { + var popupMenu = new PopupMenu(theme); + + var openMenu = popupMenu.CreateMenuItem("Open".Localize()); + openMenu.Click += (s2, e2) => + { + popupMenu.Close(); + ShowOpenDialog(imageObject); + }; + + popupMenu.CreateSeparator(); + + var copyMenu = popupMenu.CreateMenuItem("Copy".Localize()); + copyMenu.Click += (s2, e2) => + { + Clipboard.Instance.SetImage(imageObject.Image); + }; + + var pasteMenu = popupMenu.CreateMenuItem("Paste".Localize()); + pasteMenu.Click += (s2, e2) => + { + var activeImage = Clipboard.Instance.GetImage(); + + // Persist + string filePath = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".png"); + ImageIO.SaveImageData( + filePath, + activeImage); + + imageObject.AssetPath = filePath; + imageObject.Mesh = null; + + requestWidgetUpdate(); + + imageObject.Invalidate(InvalidateType.Image); + }; + + pasteMenu.Enabled = Clipboard.Instance.ContainsImage; + + popupMenu.ShowMenu(widget, e); + } + }; + } + + public static void ShowOpenDialog(IAssetObject assetObject) + { + UiThread.RunOnIdle(() => + { + // we do this using to make sure that the stream is closed before we try and insert the Picture + AggContext.FileDialogs.OpenFileDialog( + new OpenFileDialogParams( + "Select an image file|*.jpg;*.png;*.bmp;*.gif;*.pdf", + multiSelect: false, + title: "Add Image".Localize()), + (openParams) => + { + if (!File.Exists(openParams.FileName)) + { + return; + } + + assetObject.AssetPath = openParams.FileName; + }); + }); + } + } +} \ No newline at end of file diff --git a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs index cadedc09e..63789d6f7 100644 --- a/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs +++ b/MatterControlLib/Library/Providers/MatterControl/PrimitivesContainer.cs @@ -120,6 +120,10 @@ namespace MatterHackers.MatterControl.Library "Dual Contouring".Localize(), async () => await DualContouringObject3D.Create()) { DateCreated = new DateTime(index++) }, + new GeneratorItem( + "QR Code".Localize(), + async () => await QrCodeObject3D.Create()) + { DateCreated = new DateTime(index++) }, #endif new GeneratorItem( "Image Converter".Localize(), diff --git a/MatterControlLib/MatterControlLib.csproj b/MatterControlLib/MatterControlLib.csproj index b586235d1..880bf10c1 100644 --- a/MatterControlLib/MatterControlLib.csproj +++ b/MatterControlLib/MatterControlLib.csproj @@ -4,6 +4,7 @@ net6.0-windows MatterHackers Inc. 2.20.12 + true @@ -104,6 +105,7 @@ + diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index 70cb79c48..53088e113 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -3916,6 +3916,9 @@ Translated:px English:Pyramid Translated:Pyramid +English:QR Code +Translated:QR Code + English:Quality Translated:Quality diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index 7e79fdb5d..fe32884d5 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit 7e79fdb5d6d0ff90eb5d24f84716c62e5fb048df +Subproject commit fe32884d5958d2666f3912615f90234271b2471f diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 0d0d08f1c..5d6079f19 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 0d0d08f1cb4c56b977354b246fd002deb98ba8f7 +Subproject commit 5d6079f193aadd944321af96244700691d84e0f9 diff --git a/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs b/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs index 383313202..c57abae88 100644 --- a/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs +++ b/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs @@ -16,6 +16,34 @@ using TestInvoker; namespace MatterHackers.MatterControl.Tests.Automation { + [TestFixture] + public class SheetDataTests + { + [Test] + public void Calculations() + { + var sheetData = new SheetData(4, 4); + + sheetData[0, 0].Expression = "=4*2"; + + Assert.AreEqual("8", sheetData.EvaluateExpression("A1")); + Assert.AreEqual("8", sheetData.EvaluateExpression("a1")); + + sheetData["a2"].Expression = "=max(4, 5)"; + sheetData.Recalculate(); + Assert.AreEqual("5", sheetData.EvaluateExpression("a2")); + + sheetData["a3"].Expression = "=a1+a2"; + sheetData.Recalculate(); + Assert.AreEqual("13", sheetData.EvaluateExpression("a3")); + + sheetData["a4"].Expression = "=((4+5)/3+7)/5"; + sheetData.Recalculate(); + Assert.AreEqual("2", sheetData.EvaluateExpression("a4")); + + } + } + [TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)] public class PrimitiveAndSheetsTests { From 6e4ac4ec60a63e45d23b48b59b1e1d6bd51117d0 Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Fri, 25 Nov 2022 11:21:56 -0800 Subject: [PATCH 5/6] layout --- .../SlicerConfiguration/UIFields/EnumDisplayField.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MatterControlLib/SlicerConfiguration/UIFields/EnumDisplayField.cs b/MatterControlLib/SlicerConfiguration/UIFields/EnumDisplayField.cs index 20009a68d..66ef6fa76 100644 --- a/MatterControlLib/SlicerConfiguration/UIFields/EnumDisplayField.cs +++ b/MatterControlLib/SlicerConfiguration/UIFields/EnumDisplayField.cs @@ -453,7 +453,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration { Padding = PopupMenu.MenuPadding, VAnchor = VAnchor.Center, - }; + Margin = new BorderDouble(8, 1), + }; return popupMenu.CreateButtonSelectMenuItem(textWidget, text, buttonKvps, startingValue, setter, minSpacerWidth); } @@ -468,7 +469,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration { Padding = PopupMenu.MenuPadding, VAnchor = VAnchor.Center, - }; + Margin = new BorderDouble(8, 1), + }; return popupMenu.CreateButtonMenuItem(textWidget, text, buttonKvps, minSpacerWidth); } From e95fd6f388eee3aa21e5f9a2a40976007384305d Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Fri, 25 Nov 2022 13:11:27 -0800 Subject: [PATCH 6/6] Made text work better in sheets Added WiFi login to qr code generator --- .../Primitives/ComponentObject3D.cs | 2 +- .../DesignTools/Primitives/QrCodeObject3D.cs | 63 +++++++++++++++++-- .../DesignTools/Sheets/SheetData.cs | 62 ++++++++++++++++-- .../DesignTools/Sheets/SheetObject3D.cs | 59 +++++++---------- .../View3D/Actions/SheetFieldWidget.cs | 10 +-- StaticData/Translations/Master.txt | 9 +++ .../DesignTools/SheetTests.cs | 39 +++++++----- 7 files changed, 176 insertions(+), 68 deletions(-) diff --git a/MatterControlLib/DesignTools/Primitives/ComponentObject3D.cs b/MatterControlLib/DesignTools/Primitives/ComponentObject3D.cs index 1744ea2a0..b61ff8c65 100644 --- a/MatterControlLib/DesignTools/Primitives/ComponentObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/ComponentObject3D.cs @@ -153,7 +153,7 @@ namespace MatterHackers.MatterControl.DesignTools if (firtSheet != null) { // We don't have any cache of the cell content, get the current content - double.TryParse(firtSheet.SheetData.EvaluateExpression(cellId2), out double value); + double.TryParse(firtSheet.SheetData.GetCellValue(cellId2), out double value); cellData2 = value.ToString(); } } diff --git a/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs index b6842a54e..fdf07f696 100644 --- a/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/QrCodeObject3D.cs @@ -47,7 +47,7 @@ using QRCoder; namespace MatterHackers.MatterControl.DesignTools { [HideMeterialAndColor] - public class QrCodeObject3D : Object3D, IImageProvider, IObject3DControlsProvider + public class QrCodeObject3D : Object3D, IImageProvider, IObject3DControlsProvider, IPropertyGridModifier { private const double DefaultSizeMm = 60; @@ -69,6 +69,33 @@ namespace MatterHackers.MatterControl.DesignTools public override bool CanApply => false; + public enum QrCodeTypes + { + Text, + WiFi + } + + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public QrCodeTypes OutputOption { get; set; } = QrCodeTypes.Text; + + + // WIFI:S:;T:;P:;H:;; + [Description("The name of the WiFi network")] + public StringOrExpression SSID { get; set; } = ""; + + [Description("The password of the WiFi network")] + public StringOrExpression Password { get; set; } = ""; + + public enum SecurityTypes + { + WEP, + WPA, + None + } + + [EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)] + public SecurityTypes Security { get; set; } = SecurityTypes.WPA; + public StringOrExpression Text { get; set; } = "https://www.matterhackers.com"; [DisplayName("")] @@ -247,8 +274,28 @@ namespace MatterHackers.MatterControl.DesignTools private ImageBuffer BuildImage() { - QRCodeGenerator qrGenerator = new QRCodeGenerator(); - QRCodeData qrCodeData = qrGenerator.CreateQrCode(Text.Value(this), QRCodeGenerator.ECCLevel.Q); + var text = Text.Value(this); + + if (OutputOption == QrCodeTypes.WiFi) + { + var ssid = SSID.Value(this).Replace(":", "\\:"); + var security = ""; + switch(Security) + { + case SecurityTypes.WPA: + security = "WPA"; + break; + case SecurityTypes.WEP: + security = "WEP"; + break; + } + var password = Password.Value(this).Replace(":", "\\:"); + + text = $"WIFI:S:{ssid};T:{security};P:{password};H:;;"; + } + + QRCodeGenerator qrGenerator = new QRCodeGenerator(); + QRCodeData qrCodeData = qrGenerator.CreateQrCode(text, QRCodeGenerator.ECCLevel.Q); QRCode qrCode = new QRCode(qrCodeData); System.Drawing.Bitmap qrCodeImage = qrCode.GetGraphic(16); @@ -471,5 +518,13 @@ namespace MatterHackers.MatterControl.DesignTools }); }); } - } + + public void UpdateControls(PublicPropertyChange change) + { + change.SetRowVisible(nameof(Text), () => OutputOption == QrCodeTypes.Text); + change.SetRowVisible(nameof(SSID), () => OutputOption == QrCodeTypes.WiFi); + change.SetRowVisible(nameof(Password), () => OutputOption == QrCodeTypes.WiFi); + change.SetRowVisible(nameof(Security), () => OutputOption == QrCodeTypes.WiFi); + } + } } \ No newline at end of file diff --git a/MatterControlLib/DesignTools/Sheets/SheetData.cs b/MatterControlLib/DesignTools/Sheets/SheetData.cs index 8c75792ef..b7d293c1a 100644 --- a/MatterControlLib/DesignTools/Sheets/SheetData.cs +++ b/MatterControlLib/DesignTools/Sheets/SheetData.cs @@ -32,6 +32,7 @@ using org.mariuszgromada.math.mxparser; using Newtonsoft.Json; using System.Linq; using System; +using g3; namespace MatterHackers.MatterControl.DesignTools { @@ -54,7 +55,37 @@ namespace MatterHackers.MatterControl.DesignTools } } - public string EvaluateExpression(string expression) + public string GetCellValue(string cellId) + { + lock (locker) + { + if (!tabelCalculated) + { + BuildTableConstants(); + } + + var cell = this[cellId]; + + var expression = cell.Expression; + + if (expression.StartsWith("=")) + { + expression = expression.Substring(1); + var evaluator = new Expression(expression.ToLower()); + AddConstants(evaluator); + var value = evaluator.calculate(); + + return value.ToString(); + } + else + { + // return the expression without evaluation + return expression; + } + } + } + + public string EvaluateExpression(string expression) { lock (locker) { @@ -65,9 +96,21 @@ namespace MatterHackers.MatterControl.DesignTools if(expression.StartsWith("=")) { - expression = expression.Substring(1); - } - var evaluator = new Expression(expression.ToLower()); + expression = expression.Substring(1).Trim(); + + // if it is a direct cell reference than return that cells value + if (expression.Length == 2) + { + var column = (uint)expression.Substring(0, 1).ToUpper()[0] - 'A'; + var row = (uint)expression.Substring(1, 1)[0] - '1'; + if (column < Width && row < Height) + { + return GetCellValue(CellId((int)column, (int)row)); + } + } + } + + var evaluator = new Expression(expression.ToLower()); AddConstants(evaluator); var value = evaluator.calculate(); @@ -83,6 +126,12 @@ namespace MatterHackers.MatterControl.DesignTools } } + /// + /// Return the cell at the given position + /// + /// The x index + /// The y index + /// The TableCell public TableCell this[int x, int y] { get @@ -102,6 +151,11 @@ namespace MatterHackers.MatterControl.DesignTools } } + /// + /// Get the Table cell for the given cellId + /// + /// + /// The table cell for the id public TableCell this[string cellId] { get diff --git a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs index 255f4cd92..3d6404fc2 100644 --- a/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs +++ b/MatterControlLib/DesignTools/Sheets/SheetObject3D.cs @@ -479,15 +479,34 @@ namespace MatterHackers.MatterControl.DesignTools inputExpression = ReplaceConstantsWithValues(owner, inputExpression); - // check if the expression is not an equation (does not start with "=") - if (inputExpression.Length > 0 && inputExpression[0] != '=') + // check if the expression is an equation (starts with "=") + if (inputExpression.Length > 0 && inputExpression[0] == '=') + { + // look through all the parents + var sheet = FindFirstSheet(owner); + if (sheet != null) + { + // try to manage the cell into the correct data type + string value = sheet.SheetData.EvaluateExpression(inputExpression); + return CastResult(value, inputExpression); + } + + // could not find a sheet, try to evaluate the expression directly + var evaluator = new Expression(inputExpression.ToLower()); + if (evaluator.checkSyntax()) + { + Debug.WriteLine(evaluator.getErrorMessage()); + } + + return CastResult(evaluator.calculate().ToString(), inputExpression); + } + else // not an equation so try to parse it directly { if (typeof(T) == typeof(string)) { return (T)(object)inputExpression; } - // not an equation so try to parse it directly if (double.TryParse(inputExpression, out var result)) { if (typeof(T) == typeof(double)) @@ -499,41 +518,9 @@ namespace MatterHackers.MatterControl.DesignTools return (T)(object)(int)Math.Round(result); } } - else - { - if (typeof(T) == typeof(double)) - { - return (T)(object)0.0; - } - if (typeof(T) == typeof(int)) - { - return (T)(object)0; - } - } - } - - if (inputExpression.Length > 0 && inputExpression[0] == '=') - { - inputExpression = inputExpression.Substring(1); - } - // look through all the parents - var sheet = FindFirstSheet(owner); - if (sheet != null) - { - // try to manage the cell into the correct data type - string value = sheet.SheetData.EvaluateExpression(inputExpression); - return CastResult(value, inputExpression); + return (T)(object)0; } - - // could not find a sheet, try to evaluate the expression directly - var evaluator = new Expression(inputExpression.ToLower()); - if(evaluator.checkSyntax()) - { - Debug.WriteLine(evaluator.getErrorMessage()); - } - - return CastResult(evaluator.calculate().ToString(), inputExpression); } /// diff --git a/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetFieldWidget.cs b/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetFieldWidget.cs index d9f850262..b8f2bc425 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetFieldWidget.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/Actions/SheetFieldWidget.cs @@ -80,15 +80,7 @@ namespace MatterHackers.MatterControl.DesignTools private void UpdateContents() { - var expression = SheetData[x, y].Expression; - if (expression.StartsWith("=")) - { - content.Text = SheetData.EvaluateExpression(expression); - } - else - { - content.Text = expression; - } + content.Text = SheetData.GetCellValue(SheetData.CellId(x, y)); } public override void OnKeyPress(KeyPressEventArgs keyPressEvent) diff --git a/StaticData/Translations/Master.txt b/StaticData/Translations/Master.txt index 53088e113..1fe5c47d0 100644 --- a/StaticData/Translations/Master.txt +++ b/StaticData/Translations/Master.txt @@ -3415,6 +3415,9 @@ Translated:Output English:Output only the first layer of the print. Especially useful for outputting gcode data for applications like engraving or cutting. Translated:Output only the first layer of the print. Especially useful for outputting gcode data for applications like engraving or cutting. +English:Output Option +Translated:Output Option + English:Output Resolution Translated:Output Resolution @@ -4423,6 +4426,9 @@ Translated:seconds English:Sections Translated:Sections +English:Security +Translated:Security + English:Segments Translated:Segments @@ -4861,6 +4867,9 @@ Translated:Split English:Split Mesh Translated:Split Mesh +English:SSID +Translated:SSID + English:Stable Translated:Stable diff --git a/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs b/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs index c57abae88..e157361f7 100644 --- a/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs +++ b/Tests/MatterControl.AutomationTests/DesignTools/SheetTests.cs @@ -24,25 +24,36 @@ namespace MatterHackers.MatterControl.Tests.Automation { var sheetData = new SheetData(4, 4); - sheetData[0, 0].Expression = "=4*2"; + void Test(string cell, string expression, string expected) + { + sheetData[cell].Expression = expression; + sheetData.Recalculate(); + Assert.AreEqual(expected, sheetData.GetCellValue(cell)); + } - Assert.AreEqual("8", sheetData.EvaluateExpression("A1")); - Assert.AreEqual("8", sheetData.EvaluateExpression("a1")); + // simple multiply retrived upper and lower case + Test("A1", "=4*2", "8"); + Test("a1", "=4*2", "8"); - sheetData["a2"].Expression = "=max(4, 5)"; - sheetData.Recalculate(); - Assert.AreEqual("5", sheetData.EvaluateExpression("a2")); + // make sure functions are working, max in this case + Test("a2", "=max(4, 5)", "5"); - sheetData["a3"].Expression = "=a1+a2"; - sheetData.Recalculate(); - Assert.AreEqual("13", sheetData.EvaluateExpression("a3")); + // make sure cell references are working + Test("a3", "=a1+a2", "13"); - sheetData["a4"].Expression = "=((4+5)/3+7)/5"; - sheetData.Recalculate(); - Assert.AreEqual("2", sheetData.EvaluateExpression("a4")); + // complex formulas are working + Test("a4", "=((4+5)/3+7)/5", "2"); - } - } + // complex formulas with references are working + Test("b1", "=(a4+a3)*.5", "7.5"); + + // constants work, like pi + Test("b2", "=pi", "3.141592653589793"); + + // check that we get string data back unmodified + Test("b3", "hello", "hello"); + } + } [TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)] public class PrimitiveAndSheetsTests