From 7ec4fc70ea22e24573fe8547f6e8524989d636cc Mon Sep 17 00:00:00 2001 From: Michael Kirschner <508936+mjkkirschner@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:57:29 -0500 Subject: [PATCH] DYN-5965: introduce new string from nodes that accept numeric format specifiers (#14369) passed here: https://master-5.jenkins.autodesk.com/job/Dynamo/job/DynamoSelfServe/job/pullRequestValidation/16776/ merging in for testing --- ...CoreNodeModels.FormattedStringFromArray.md | 17 + ...oreNodeModels.FormattedStringFromObject.md | 17 + .../Configuration/PreferenceSettings.cs | 10 +- src/DynamoCore/Graph/Nodes/NodeModel.cs | 19 +- src/DynamoCore/Graph/Nodes/TypeLoadData.cs | 12 +- src/DynamoCore/Library/FunctionDescriptor.cs | 42 +- src/DynamoCore/Library/LibraryServices.cs | 4 +- src/DynamoCore/Models/DynamoModel.cs | 5 + .../Properties/Resources.Designer.cs | 19 +- .../Properties/Resources.en-US.resx | 5 +- src/DynamoCore/Properties/Resources.resx | 5 +- src/DynamoCore/PublicAPI.Unshipped.txt | 1 + .../NodeModelSearchElementBase.cs | 1 + .../SearchElements/NodeSearchElement.cs | 10 + .../SearchElements/ZeroTouchSearchElement.cs | 2 + src/DynamoCoreWpf/PublicAPI.Unshipped.txt | 1 + .../ViewModels/Core/NodeViewModel.cs | 17 +- src/DynamoCoreWpf/Views/Core/NodeView.xaml | 15 + .../ProtoCore/DSASM/Mirror/ExecutionMirror.cs | 88 +- src/Engine/ProtoCore/FFI/CLRDLLModule.cs | 8 + .../ProtoCore/Lang/BuiltInFunctionEndPoint.cs | 24 +- src/Engine/ProtoCore/Lang/BuiltInMethods.cs | 42 +- src/Engine/ProtoCore/Parser/AssociativeAST.cs | 4 + src/Engine/ProtoCore/Utils/StringUtils.cs | 37 +- .../Properties/Resources.Designer.cs | 12 +- .../Properties/Resources.en-US.resx | 6 +- .../CoreNodeModels/Properties/Resources.resx | 6 +- src/Libraries/CoreNodeModels/String.cs | 87 +- .../Packages/LibrarieJS/layoutSpecs.json | 31 +- test/DynamoCoreTests/CodeBlockNodeTests.cs | 11 +- .../DSEvaluationUnitTestBase.cs | 7 +- .../DynamoCoreTests/ExperimentalNodesTests.cs | 37 + test/DynamoCoreTests/LibraryTests.cs | 20 +- test/DynamoCoreTests/Nodes/StringTests.cs | 72 + .../NodeViewCustomizationTests.cs | 4 +- test/Engine/FFITarget/ExperimentalNodes.cs | 24 + .../ProtoTest/TD/MultiLangTests/StringTest.cs | 65 + .../MarkdownGeneratorCommandTests.cs | 10 +- ...CoreNodeModels.FormattedStringFromArray.md | 5 + ...oreNodeModels.FormattedStringFromObject.md | 5 + .../CoreNodeModels.FromArray.md | 4 +- .../CoreNodeModels.FromObject.md | 4 +- ...estNumberToString_normal_numericformat.dyn | 1195 +++++++++++++++++ ...stStringFromArrayPreview_numericformat.dyn | 526 ++++++++ 44 files changed, 2448 insertions(+), 88 deletions(-) create mode 100644 doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromArray.md create mode 100644 doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromObject.md create mode 100644 test/DynamoCoreTests/ExperimentalNodesTests.cs create mode 100644 test/Engine/FFITarget/ExperimentalNodes.cs create mode 100644 test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromArray.md create mode 100644 test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromObject.md create mode 100644 test/core/string/TestNumberToString_normal_numericformat.dyn create mode 100644 test/core/string/TestStringFromArrayPreview_numericformat.dyn diff --git a/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromArray.md b/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromArray.md new file mode 100644 index 00000000000..76a62492255 --- /dev/null +++ b/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromArray.md @@ -0,0 +1,17 @@ +## In Depth +This node will convert an oject to a string. The second input `format specifier` controls how numeric inputs are converted to their string representations. +This `format specifier` inputs should be one of the c# standard format numeric specifiers. + +format specifiers should be in the form: +`` for example F1 + +Some commonly used format specifiers are: +``` +G : general formatting G 1000.0 -> "1000" +F : fixed point notation F4 1000.0 -> "1000.0000" +N : number N2 1000 -> "1,000.00" +``` + +The default for this node is `G`, which will output a compact, but variable representation. + +[see the microsoft documentation for more detailed information.](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#standard-format-specifiers) diff --git a/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromObject.md b/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromObject.md new file mode 100644 index 00000000000..76a62492255 --- /dev/null +++ b/doc/distrib/NodeHelpFiles/CoreNodeModels.FormattedStringFromObject.md @@ -0,0 +1,17 @@ +## In Depth +This node will convert an oject to a string. The second input `format specifier` controls how numeric inputs are converted to their string representations. +This `format specifier` inputs should be one of the c# standard format numeric specifiers. + +format specifiers should be in the form: +`` for example F1 + +Some commonly used format specifiers are: +``` +G : general formatting G 1000.0 -> "1000" +F : fixed point notation F4 1000.0 -> "1000.0000" +N : number N2 1000 -> "1,000.00" +``` + +The default for this node is `G`, which will output a compact, but variable representation. + +[see the microsoft documentation for more detailed information.](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#standard-format-specifiers) diff --git a/src/DynamoCore/Configuration/PreferenceSettings.cs b/src/DynamoCore/Configuration/PreferenceSettings.cs index 88d8897b275..8dfbf926447 100644 --- a/src/DynamoCore/Configuration/PreferenceSettings.cs +++ b/src/DynamoCore/Configuration/PreferenceSettings.cs @@ -112,6 +112,10 @@ private readonly static Lazy /// public static readonly DateTime DynamoDefaultTime = new DateTime(1977, 4, 12, 12, 12, 0, 0); + internal static readonly IEnumerable InitialExperimentalLib_Namespaces = + [ + "ProtoGeometry.dll:Autodesk.DesignScript.Geometry.PanelSurface" + ]; #endregion // The following settings are persistent between Dynamo sessions and are user-controllable @@ -1186,11 +1190,7 @@ internal void InitializeNamespacesToExcludeFromLibrary() { if (!NamespacesToExcludeFromLibrarySpecified) { - NamespacesToExcludeFromLibrary = new List() - { - "ProtoGeometry.dll:Autodesk.DesignScript.Geometry.TSpline", - "ProtoGeometry.dll:Autodesk.DesignScript.Geometry.PanelSurface" - }; + NamespacesToExcludeFromLibrary = InitialExperimentalLib_Namespaces.ToList(); NamespacesToExcludeFromLibrarySpecified = true; } } diff --git a/src/DynamoCore/Graph/Nodes/NodeModel.cs b/src/DynamoCore/Graph/Nodes/NodeModel.cs index 8131c81469a..084a2dd8030 100644 --- a/src/DynamoCore/Graph/Nodes/NodeModel.cs +++ b/src/DynamoCore/Graph/Nodes/NodeModel.cs @@ -2,17 +2,18 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Xml; -using Autodesk.DesignScript.Runtime; using Dynamo.Configuration; using Dynamo.Engine; using Dynamo.Engine.CodeGeneration; using Dynamo.Graph.Connectors; using Dynamo.Graph.Nodes.CustomNodes; +using Dynamo.Graph.Nodes.ZeroTouch; using Dynamo.Graph.Workspaces; using Dynamo.Migration; using Dynamo.Scheduler; @@ -2869,6 +2870,22 @@ public bool ShouldDisplayPreview protected bool ShouldDisplayPreviewCore { get; set; } + [Experimental("NM_ISEXPERIMENTAL_GLPYH")] + internal bool IsExperimental + { + get + { + //TODO switch on model type? + if (this is DSFunction ztnm) + { + return ztnm.Controller.Definition.IsExperimental; + } + else + { + return TypeLoadData.CheckExperimentalFromAttribute(GetType()); + } + } + } public event Action RenderPackagesUpdated; private void OnRenderPackagesUpdated(RenderPackageCache packages) diff --git a/src/DynamoCore/Graph/Nodes/TypeLoadData.cs b/src/DynamoCore/Graph/Nodes/TypeLoadData.cs index 82dbca9e547..06d2cf0df42 100644 --- a/src/DynamoCore/Graph/Nodes/TypeLoadData.cs +++ b/src/DynamoCore/Graph/Nodes/TypeLoadData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -95,6 +95,12 @@ public TypeLoadData(Type typeIn) OutputParameters = Type.GetCustomAttributes(false) .SelectMany(x => x.PortTypes); + IsExperimental = CheckExperimentalFromAttribute(Type); + } + + internal static bool CheckExperimentalFromAttribute(System.Type type) + { + return type.GetCustomAttributes(false).Any(); } /// @@ -166,5 +172,9 @@ public string Category /// Indicates output parameters. /// public readonly IEnumerable OutputParameters; + /// + /// Is this type experimental/unstable. + /// + internal bool IsExperimental; } } diff --git a/src/DynamoCore/Library/FunctionDescriptor.cs b/src/DynamoCore/Library/FunctionDescriptor.cs index b0233dc1f03..15d3dc83ef4 100644 --- a/src/DynamoCore/Library/FunctionDescriptor.cs +++ b/src/DynamoCore/Library/FunctionDescriptor.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text; +using Dynamo.Configuration; using Dynamo.Graph.Nodes; using Dynamo.Interfaces; using Dynamo.Library; @@ -145,7 +146,12 @@ public FunctionDescriptorParams() /// /// Indicates if the lacing strategy is disabled on the function /// - public bool IsLacingDisabled { get; set; } + public bool IsLacingDisabled { get; set; } + //TODO - should this somehow contain more info - ExperimentalInfo{IsExperimental, ExperimentalMessage/url}?} + /// + /// Experimental/Unstable function + /// + internal bool IsExperimental { get; set; } } /// @@ -186,7 +192,7 @@ public FunctionDescriptor(FunctionDescriptorParams funcDescParams) var type = funcDescParams.FunctionType; var inputParameters = new List>(); //Add instance parameter as one of the inputs for instance method as well as properties. - if(type == FunctionType.InstanceMethod || type == FunctionType.InstanceProperty) + if (type == FunctionType.InstanceMethod || type == FunctionType.InstanceProperty) inputParameters.Add(Tuple.Create(UnqualifedClassName.ToLower(), UnqualifedClassName)); if (Parameters.Any()) @@ -196,7 +202,7 @@ public FunctionDescriptor(FunctionDescriptorParams funcDescParams) } InputParameters = inputParameters; - ReturnType = funcDescParams.ReturnType; + ReturnType = funcDescParams.ReturnType; Type = type; ReturnKeys = funcDescParams.ReturnKeys; IsVarArg = funcDescParams.IsVarArg; @@ -206,6 +212,7 @@ public FunctionDescriptor(FunctionDescriptorParams funcDescParams) IsBuiltIn = funcDescParams.IsBuiltIn; IsPackageMember = funcDescParams.IsPackageMember; IsLacingDisabled = funcDescParams.IsLacingDisabled; + IsExperimental = funcDescParams.IsExperimental || CheckIfFunctionIsMarkedExperimentalByPrefs(this); } /// @@ -320,28 +327,28 @@ public string Category { var categoryBuf = new StringBuilder(); categoryBuf.Append(GetRootCategory()); - + //if this is not BuiltIn function search NodeCategoryAttribute for it - if (ClassName!=null) + if (ClassName != null) { //get function assembly var asm = AppDomain.CurrentDomain.GetAssemblies() .Where(x => x.GetName().Name == Path.GetFileNameWithoutExtension(Assembly)) .ToArray(); - if (asm.Any() && asm.First().GetType(ClassName)!=null) + if (asm.Any() && asm.First().GetType(ClassName) != null) { //get class type of function var type = asm.First().GetType(ClassName); //get NodeCategoryAttribute for this function if it was been defined - var nodeCat = type.GetMethods().Where(x=>x.Name==FunctionName) - .Select(x => x.GetCustomAttribute(typeof (NodeCategoryAttribute))) - .Where(x=>x!=null) + var nodeCat = type.GetMethods().Where(x => x.Name == FunctionName) + .Select(x => x.GetCustomAttribute(typeof(NodeCategoryAttribute))) + .Where(x => x != null) .Cast() - .Select(x=>x.ElementCategory) + .Select(x => x.ElementCategory) .FirstOrDefault(); - + //if attribute is found compose node category string with last part from attribute if (!string.IsNullOrEmpty(nodeCat) && ( nodeCat == LibraryServices.Categories.Constructors @@ -353,7 +360,7 @@ public string Category } } } - + switch (Type) { case FunctionType.Constructor: @@ -567,5 +574,16 @@ private string GetRootCategory() return string.IsNullOrEmpty(Namespace) ? filename : filename + "." + Namespace; } + private bool CheckIfFunctionIsMarkedExperimentalByPrefs(FunctionDescriptor fd) + { + if (PreferenceSettings.InitialExperimentalLib_Namespaces. + Where(x => x.StartsWith(fd.Assembly + ":")).Select(x => x.Split(":").LastOrDefault()).Any(nsp => fd.QualifiedName.StartsWith(nsp))) + { + return true; + } + return false; + } + internal bool IsExperimental { get;} } + } diff --git a/src/DynamoCore/Library/LibraryServices.cs b/src/DynamoCore/Library/LibraryServices.cs index 6b78418f09e..f48cf117c20 100644 --- a/src/DynamoCore/Library/LibraryServices.cs +++ b/src/DynamoCore/Library/LibraryServices.cs @@ -1083,9 +1083,9 @@ private void ImportProcedure(string library, ProcedureNode proc) IsBuiltIn = pathManager.PreloadedLibraries.Contains(library) || library.StartsWith(PathManager.BuiltinPackagesDirectory), IsPackageMember = packagedLibraries.Contains(library), - IsLacingDisabled = isLacingDisabled + IsLacingDisabled = isLacingDisabled, + IsExperimental = (methodAttribute?.IsExperimental).GetValueOrDefault()|| (classAttribute?.IsExperimental).GetValueOrDefault() }); - AddImportedFunctions(library, new[] { function }); } diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs index c1b267cd378..349ceb8a112 100644 --- a/src/DynamoCore/Models/DynamoModel.cs +++ b/src/DynamoCore/Models/DynamoModel.cs @@ -3399,6 +3399,11 @@ private NodeModelSearchElement AddNodeTypeToSearch(TypeLoadData typeLoadData) { return null; } + if(PreferenceSettings.InitialExperimentalLib_Namespaces. + Select(x => x.Split(":").LastOrDefault()).Any(x => x.Contains(typeLoadData.Category))){ + //TODO safer way to set this? + typeLoadData.IsExperimental = true; + } var node = new NodeModelSearchElement(typeLoadData); SearchModel?.Add(node); diff --git a/src/DynamoCore/Properties/Resources.Designer.cs b/src/DynamoCore/Properties/Resources.Designer.cs index 61cfe00c6b3..7935c0f1ab3 100644 --- a/src/DynamoCore/Properties/Resources.Designer.cs +++ b/src/DynamoCore/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace Dynamo.Properties -{ - - +namespace Dynamo.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -497,6 +497,15 @@ public static string DllLoadException { } } + /// + /// Looks up a localized string similar to This node is currently experimental. Its behavior, name, and signature are subject to change.. + /// + public static string DocsExperimentalPrefixMessage { + get { + return ResourceManager.GetString("DocsExperimentalPrefixMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Download latest version. /// diff --git a/src/DynamoCore/Properties/Resources.en-US.resx b/src/DynamoCore/Properties/Resources.en-US.resx index 6af0906a03e..95a14871ccd 100644 --- a/src/DynamoCore/Properties/Resources.en-US.resx +++ b/src/DynamoCore/Properties/Resources.en-US.resx @@ -920,4 +920,7 @@ This package likely contains an assembly that is blocked. You will need to load default input name, rename me! - + + This node is currently experimental. Its behavior, name, and signature are subject to change. + + \ No newline at end of file diff --git a/src/DynamoCore/Properties/Resources.resx b/src/DynamoCore/Properties/Resources.resx index 0b04d7adc50..bef1c049ee2 100644 --- a/src/DynamoCore/Properties/Resources.resx +++ b/src/DynamoCore/Properties/Resources.resx @@ -923,4 +923,7 @@ This package likely contains an assembly that is blocked. You will need to load default input name, rename me! - + + This node is currently experimental. Its behavior, name, and signature are subject to change. + + \ No newline at end of file diff --git a/src/DynamoCore/PublicAPI.Unshipped.txt b/src/DynamoCore/PublicAPI.Unshipped.txt index bb911e51714..a0b5b30c8af 100644 --- a/src/DynamoCore/PublicAPI.Unshipped.txt +++ b/src/DynamoCore/PublicAPI.Unshipped.txt @@ -2782,6 +2782,7 @@ static Dynamo.Properties.Resources.DescriptionResource1.get -> string static Dynamo.Properties.Resources.DirectoryNotFound.get -> string static Dynamo.Properties.Resources.DisplayEngineFailureMessageDescription.get -> string static Dynamo.Properties.Resources.DllLoadException.get -> string +static Dynamo.Properties.Resources.DocsExperimentalPrefixMessage.get -> string static Dynamo.Properties.Resources.DownloadLatestButton.get -> string static Dynamo.Properties.Resources.DSFunctionNodeDescription.get -> string static Dynamo.Properties.Resources.DulicatedPackage.get -> string diff --git a/src/DynamoCore/Search/SearchElements/NodeModelSearchElementBase.cs b/src/DynamoCore/Search/SearchElements/NodeModelSearchElementBase.cs index 8ac8fa2943c..8bd914983f8 100644 --- a/src/DynamoCore/Search/SearchElements/NodeModelSearchElementBase.cs +++ b/src/DynamoCore/Search/SearchElements/NodeModelSearchElementBase.cs @@ -39,6 +39,7 @@ protected NodeModelSearchElementBase(TypeLoadData typeLoadData) { ElementType |= ElementTypes.BuiltIn; } + IsExperimental = typeLoadData.IsExperimental; } } } diff --git a/src/DynamoCore/Search/SearchElements/NodeSearchElement.cs b/src/DynamoCore/Search/SearchElements/NodeSearchElement.cs index 0d1751345d4..d286e2ff02f 100644 --- a/src/DynamoCore/Search/SearchElements/NodeSearchElement.cs +++ b/src/DynamoCore/Search/SearchElements/NodeSearchElement.cs @@ -3,6 +3,7 @@ using System.Linq; using Dynamo.Configuration; using Dynamo.Graph.Nodes; +using Dynamo.Properties; namespace Dynamo.Search.SearchElements { @@ -19,6 +20,7 @@ public abstract class NodeSearchElement : ISearchEntry, ISource, IClo private SearchElementGroup group; private string assembly; private bool isVisibleInSearch = true; + internal virtual bool IsExperimental { get; set; } internal AutoCompletionNodeElementInfo AutoCompletionNodeElementInfo { get; set; } = new AutoCompletionNodeElementInfo(); @@ -141,7 +143,15 @@ public string Description get { if (string.IsNullOrEmpty(description)) + { return Configurations.NoDescriptionAvailable; + } + if (IsExperimental) + { + return $"{Resources.DocsExperimentalPrefixMessage}" + + $"{Environment.NewLine}{Environment.NewLine}{description}"; + } + return description; } diff --git a/src/DynamoCore/Search/SearchElements/ZeroTouchSearchElement.cs b/src/DynamoCore/Search/SearchElements/ZeroTouchSearchElement.cs index 9831ed8e4ac..59572a7419d 100644 --- a/src/DynamoCore/Search/SearchElements/ZeroTouchSearchElement.cs +++ b/src/DynamoCore/Search/SearchElements/ZeroTouchSearchElement.cs @@ -32,6 +32,8 @@ public class ZeroTouchSearchElement : NodeSearchElement /// internal FunctionDescriptor Descriptor => functionDescriptor; + internal override bool IsExperimental => functionDescriptor.IsExperimental; + /// /// Initializes a new instance of the class /// with the DesignScript function description diff --git a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt index 357e3063136..f92d8411a69 100644 --- a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt +++ b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt @@ -2416,6 +2416,7 @@ Dynamo.ViewModels.NodeViewModel.InPorts.set -> void Dynamo.ViewModels.NodeViewModel.IsCustomFunction.get -> bool Dynamo.ViewModels.NodeViewModel.IsDisplayingLabels.get -> bool Dynamo.ViewModels.NodeViewModel.IsDisplayingLabels.set -> void +Dynamo.ViewModels.NodeViewModel.IsExperimental.get -> bool Dynamo.ViewModels.NodeViewModel.IsFrozen.get -> bool Dynamo.ViewModels.NodeViewModel.IsFrozen.set -> void Dynamo.ViewModels.NodeViewModel.IsFrozenExplicitly.get -> bool diff --git a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs index e3c9a0eb99d..e45034ac7e0 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -14,6 +15,7 @@ using Dynamo.Graph; using Dynamo.Graph.Nodes; using Dynamo.Graph.Nodes.CustomNodes; +using Dynamo.Graph.Nodes.ZeroTouch; using Dynamo.Graph.Workspaces; using Dynamo.Logging; using Dynamo.Models; @@ -269,7 +271,7 @@ public ElementState State { get { return nodeLogic.State; } } - + /// /// The total number of info/warnings/errors dismissed by the user on this node. /// This is displayed on the node by a little icon beside the Context Menu button. @@ -356,7 +358,7 @@ public bool IsInteractionEnabled get { return true; } } - [JsonProperty("ShowGeometry",Order = 6)] + [JsonProperty("ShowGeometry", Order = 6)] public bool IsVisible { get @@ -641,7 +643,16 @@ public bool IsFrozenExplicitly return false; } } - + [Experimental("NVM_ISEXPERIMENTAL_GLPYH")] + [JsonIgnore] + public bool IsExperimental + { + get + { + return NodeModel.IsExperimental && (DynamoModel.FeatureFlags?.CheckFeatureFlag("experimentalGlyphIsVisible", false) ?? false); + } + } + /// /// A flag indicating whether the underlying NodeModel's IsFrozen property can be toggled. /// diff --git a/src/DynamoCoreWpf/Views/Core/NodeView.xaml b/src/DynamoCoreWpf/Views/Core/NodeView.xaml index 3febdf3b1e2..b2f5a5f9ec1 100644 --- a/src/DynamoCoreWpf/Views/Core/NodeView.xaml +++ b/src/DynamoCoreWpf/Views/Core/NodeView.xaml @@ -5,7 +5,9 @@ xmlns:ui="clr-namespace:Dynamo.UI" xmlns:dynui="clr-namespace:Dynamo.UI.Controls" xmlns:p="clr-namespace:Dynamo.Wpf.Properties" + xmlns:dp="clr-namespace:Dynamo.Properties;assembly=DynamoCore" xmlns:controls="clr-namespace:Dynamo.Views" + xmlns:fa="clr-namespace:FontAwesome5;assembly=FontAwesome5.Net" xmlns:dp1="clr-namespace:Dynamo.Properties;assembly=DynamoCore" Name="topControl" Width="Auto" Height="Auto" @@ -449,6 +451,19 @@ Style="{StaticResource SZoomFadeOutFrameworkElement}" FlowDirection="LeftToRight" Orientation="Horizontal"> + + + + + + + /// Provides reflective capabilities over the execution of a DSASM Executable @@ -44,24 +45,85 @@ public ExecutionMirror(ProtoCore.DSASM.Executive exec, ProtoCore.RuntimeCore cor MirrorTarget = exec; } + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal string GetStringValueUsingFormat(StackValue val,string formatSpecifier, Heap heap, int langblock, bool forPrint = false) + { + return GetStringValueImplementation(val,formatSpecifier, heap, langblock, -1, -1, forPrint); + } + + /// + /// + /// + /// + /// + /// + /// + /// + [Obsolete] + //TODO this is used all over the place internally + //so it's good to keep it and likely make it internal + //until we want to change formatting everywhere we process strings. public string GetStringValue(StackValue val, Heap heap, int langblock, bool forPrint = false) { return GetStringValue(val, heap, langblock, -1, -1, forPrint); } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [Obsolete] public string GetStringValue(StackValue val, Heap heap, int langblock, int maxArraySize, int maxOutputDepth, bool forPrint = false) { - if (formatParams == null) - formatParams = new OutputFormatParameters(maxArraySize, maxOutputDepth); + //use F6 to match the existing behavior. + return GetStringValueImplementation(val, StringUtils.LEGACYFORMATTING, heap, langblock, maxArraySize, maxOutputDepth, forPrint); + } + + private string GetStringValueImplementation(StackValue val, string formatSpecifier, Heap heap, int langblock, int maxArraySize, + int maxOutputDepth, bool forPrint = false) + { + var legacyBehavior = formatSpecifier == StringUtils.LEGACYFORMATTING; + formatParams ??= new OutputFormatParameters(maxArraySize, maxOutputDepth); + //if format is the dynamo format specifier then use prefs format. + if (formatSpecifier == StringUtils.DynamoPreferencesNumberFormat) + { + formatSpecifier = ProtoCore.Mirror.MirrorData.PrecisionFormat; + } + if (val.IsInteger) { - return val.IntegerValue.ToString(); + //in legacy mode we don't format ints. + if (legacyBehavior) + { + return val.IntegerValue.ToString(); + } + return val.IntegerValue.ToString(formatSpecifier); + } if (val.IsDouble) { - return val.DoubleValue.ToString("F6"); + //we always use f6 for doubles in legacy mode. + if (legacyBehavior) + { + return val.DoubleValue.ToString("F6"); + } + return val.DoubleValue.ToString(formatSpecifier); } if (val.IsNull) @@ -77,7 +139,7 @@ public string GetStringValue(StackValue val, Heap heap, int langblock, int maxAr if (val.IsArray) { HashSet pointers = new HashSet { val.ArrayPointer }; - string arrTrace = GetArrayTrace(val, heap, langblock, pointers, forPrint); + string arrTrace = GetArrayTrace(val, formatSpecifier, heap, langblock, pointers, forPrint); if (forPrint) return "[" + arrTrace + "]"; @@ -125,6 +187,8 @@ public string GetStringValue(StackValue val, Heap heap, int langblock, int maxAr return "null"; // "Value not yet supported for tracing"; } + //TODO - use format specifier when converting classes to string + [Obsolete("This will be made internal")] public string GetClassTrace(StackValue val, Heap heap, int langblock, bool forPrint) { if (!formatParams.ContinueOutputTrace()) @@ -212,7 +276,7 @@ public string GetClassTrace(StackValue val, Heap heap, int langblock, bool forPr } } - private string GetPointerTrace(StackValue ptr, Heap heap, int langblock, HashSet pointers, bool forPrint) + private string GetPointerTrace(StackValue ptr, string formatSpecifier, Heap heap, int langblock, HashSet pointers, bool forPrint) { if (pointers.Contains(ptr.ArrayPointer)) { @@ -223,13 +287,13 @@ private string GetPointerTrace(StackValue ptr, Heap heap, int langblock, HashSet if (forPrint) { - return "[" + GetArrayTrace(ptr, heap, langblock, pointers, forPrint) + "]"; + return "[" + GetArrayTrace(ptr,formatSpecifier, heap, langblock, pointers, forPrint) + "]"; } - return "[ " + GetArrayTrace(ptr, heap, langblock, pointers, forPrint) + " ]"; + return "[ " + GetArrayTrace(ptr,formatSpecifier, heap, langblock, pointers, forPrint) + " ]"; } - private string GetArrayTrace(StackValue svArray, Heap heap, int langblock, HashSet pointers, bool forPrint) + private string GetArrayTrace(StackValue svArray, string formatSpecifier, Heap heap, int langblock, HashSet pointers, bool forPrint) { if (!formatParams.ContinueOutputTrace()) return "..."; @@ -264,11 +328,11 @@ private string GetArrayTrace(StackValue svArray, Heap heap, int langblock, HashS StackValue sv = array.GetValueFromIndex(n, runtimeCore); if (sv.IsArray) { - arrayElements.Append(GetPointerTrace(sv, heap, langblock, pointers, forPrint)); + arrayElements.Append(GetPointerTrace(sv,formatSpecifier, heap, langblock, pointers, forPrint)); } else { - arrayElements.Append(GetStringValue(array.GetValueFromIndex(n, runtimeCore), heap, langblock, forPrint)); + arrayElements.Append(GetStringValueUsingFormat(array.GetValueFromIndex(n, runtimeCore),formatSpecifier, heap, langblock, forPrint)); } // If we need to truncate this array (halfArraySize > 0), and we have diff --git a/src/Engine/ProtoCore/FFI/CLRDLLModule.cs b/src/Engine/ProtoCore/FFI/CLRDLLModule.cs index bdadb618a15..5321f4be7f7 100644 --- a/src/Engine/ProtoCore/FFI/CLRDLLModule.cs +++ b/src/Engine/ProtoCore/FFI/CLRDLLModule.cs @@ -1274,6 +1274,10 @@ public FFIClassAttributes(Type type) { PreferredShortName = (attr as PreferredShortNameAttribute).PreferredShortName; } + else if (attr is System.Diagnostics.CodeAnalysis.ExperimentalAttribute) + { + IsExperimental = true; + } } } } @@ -1432,6 +1436,10 @@ public FFIMethodAttributes(MethodBase method, Dictionary case BuiltInMethods.MethodID.ToString: case BuiltInMethods.MethodID.ToStringFromObject: case BuiltInMethods.MethodID.ToStringFromArray: - ret = StringUtils.ConvertToString(formalParameters[0], runtimeCore, rmem); - break; + { + ret = StringUtils.ConvertToString(formalParameters[0], runtimeCore, rmem); + break; + } + case BuiltInMethods.MethodID.ToStringFromObjectAndFormat: + case BuiltInMethods.MethodID.ToStringFromArrayAndFormat: + { + try + { + ret = StringUtils.ConvertToString(formalParameters, runtimeCore, rmem); + } + catch(System.FormatException fe) + { + runtimeCore.RuntimeStatus.LogWarning(WarningID.InvalidArguments, fe.Message); + //TODO reuse this string instead of allocating a new one each time? + //the message could be different... + ret = StackValue.BuildString(fe.Message,runtimeCore.Heap); + } + break; + } case BuiltInMethods.MethodID.ImportData: ret = ContextDataBuiltIns.ImportData(formalParameters[0], formalParameters[1], runtimeCore, interpreter, c); break; diff --git a/src/Engine/ProtoCore/Lang/BuiltInMethods.cs b/src/Engine/ProtoCore/Lang/BuiltInMethods.cs index 40ec432ae20..8db5af10fd9 100644 --- a/src/Engine/ProtoCore/Lang/BuiltInMethods.cs +++ b/src/Engine/ProtoCore/Lang/BuiltInMethods.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM; @@ -69,8 +69,13 @@ public enum MethodID NodeAstFailed, GC, ConditionalIf, + ToStringFromObjectAndFormat, + ToStringFromArrayAndFormat, } + //this array gets accessed using the MethodID enum + //so its order is important... could be a dictionary or attributes on the enums + //to avoid confusion, easy to mess this up. private static string[] methodNames = new string[] { "AllFalse", // kAllFalse @@ -130,6 +135,8 @@ public enum MethodID Constants.kNodeAstFailed, // kNodeAstFailed "__GC", // kGC Constants.kIfConditionalMethodName, + "__ToStringFromObjectAndFormat", // kToStringFromObjectAndFormat + "__ToStringFromArrayAndFormat", // kToStringFromArrayAndFormat }; public static string GetMethodName(MethodID id) @@ -797,6 +804,7 @@ public BuiltInMethods(Core core) new KeyValuePair("object", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var, 0)), }, ID = BuiltInMethods.MethodID.ToStringFromObject, + MethodAttributes = new MethodAttributes(true, false, @"This method is obsolete, please use 'ToStringFromObjectAndFormat' "), }, new BuiltInMethod @@ -807,7 +815,35 @@ public BuiltInMethods(Core core) { new KeyValuePair("list", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var)), }.ToList(), - ID = BuiltInMethods.MethodID.ToStringFromArray + ID = BuiltInMethods.MethodID.ToStringFromArray, + MethodAttributes = new MethodAttributes(true, false, @"This method is obsolete, please use 'ToStringFromArrayAndFormat' "), + }, + + new BuiltInMethod + { + ReturnType = TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0), + Parameters = new List> + { + new KeyValuePair("object", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var, 0)), + new KeyValuePair("formatSpecifier", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0)), + //TODO (MJK) + //if we wanted to support default args for builtins we would need to support these names being parsed as binary expressions instead + //of just identifiers as is done today. We would need to add a new Parameters entry because all of this is public :( + //or we could invoke the parser directly on these strings as is done for custom node symbols... + }, + ID = BuiltInMethods.MethodID.ToStringFromObjectAndFormat, + }, + + new BuiltInMethod + { + ReturnType = TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0), + + Parameters = new [] + { + new KeyValuePair("list", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.Var)), + new KeyValuePair("formatSpecifier", TypeSystem.BuildPrimitiveTypeObject(PrimitiveType.String, 0)), + }.ToList(), + ID = BuiltInMethods.MethodID.ToStringFromArrayAndFormat }, new BuiltInMethod @@ -915,4 +951,4 @@ public BuiltInMethods(Core core) }; } } -} \ No newline at end of file +} diff --git a/src/Engine/ProtoCore/Parser/AssociativeAST.cs b/src/Engine/ProtoCore/Parser/AssociativeAST.cs index af453127a62..abf1fd72f65 100644 --- a/src/Engine/ProtoCore/Parser/AssociativeAST.cs +++ b/src/Engine/ProtoCore/Parser/AssociativeAST.cs @@ -1647,6 +1647,7 @@ public ClassAttributes(string msg = "", string preferredShortName = "") HiddenInLibrary = IsObsolete; PreferredShortName = preferredShortName; } + internal bool IsExperimental { get; set; } } public class MethodAttributes @@ -1671,6 +1672,9 @@ public IEnumerable ReturnKeys /// public string Description { get; set; } + internal bool IsExperimental { get; set; } + + public MethodAttributes(bool hiddenInLibrary = false, bool canUpdatePeriodically = false, string msg = "") { HiddenInLibrary = hiddenInLibrary; diff --git a/src/Engine/ProtoCore/Utils/StringUtils.cs b/src/Engine/ProtoCore/Utils/StringUtils.cs index d57870ff2da..0fcdbcde9f4 100644 --- a/src/Engine/ProtoCore/Utils/StringUtils.cs +++ b/src/Engine/ProtoCore/Utils/StringUtils.cs @@ -1,5 +1,6 @@ using ProtoCore.DSASM; using System; +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -7,6 +8,10 @@ namespace ProtoCore.Utils { public static class StringUtils { + //TODO consider removing this option. It makes sharing harder. + internal const string DynamoPreferencesNumberFormat = nameof(DynamoPreferencesNumberFormat); + internal const string LEGACYFORMATTING = nameof(LEGACYFORMATTING); + public static int CompareString(StackValue s1, StackValue s2, RuntimeCore runtimeCore) { if (!s1.IsString || !s2.IsString) @@ -26,12 +31,40 @@ public static string GetStringValue(StackValue sv, RuntimeCore runtimeCore) return mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true); } + //used by legacy ToString methods without format specifier. public static StackValue ConvertToString(StackValue sv, RuntimeCore runtimeCore, ProtoCore.Runtime.RuntimeMemory rmem) + { + //maintain old behavior of existing string conversion nodes by passing null for formatSpecifier. + return ConvertToStringInternal(sv, runtimeCore, null); + } + //used by new ToString methods with format specifier. + internal static StackValue ConvertToString(IEnumerable args, RuntimeCore runtimeCore, ProtoCore.Runtime.RuntimeMemory rmem) + { + if (args.Count() < 2) + { + throw new ArgumentException($"format specifier argument not defined while converting string, please report!"); + } + var sv = args.ElementAt(0); + var formatSpecifier = GetStringValue(args.ElementAt(1),runtimeCore); + return ConvertToStringInternal(sv, runtimeCore, formatSpecifier); + } + + private static StackValue ConvertToStringInternal(StackValue sv, RuntimeCore runtimeCore, string formatSpecifier) { StackValue returnSV; //TODO: Change Execution mirror class to have static methods, so that an instance does not have to be created - ProtoCore.DSASM.Mirror.ExecutionMirror mirror = new DSASM.Mirror.ExecutionMirror(new ProtoCore.DSASM.Executive(runtimeCore), runtimeCore); - returnSV = ProtoCore.DSASM.StackValue.BuildString(mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap); + ProtoCore.DSASM.Mirror.ExecutionMirror mirror = + new DSASM.Mirror.ExecutionMirror(new ProtoCore.DSASM.Executive(runtimeCore), runtimeCore); + if (formatSpecifier == null) + { + returnSV = ProtoCore.DSASM.StackValue.BuildString( + mirror.GetStringValue(sv, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap); + } + else + { + returnSV = ProtoCore.DSASM.StackValue.BuildString( + mirror.GetStringValueUsingFormat(sv, formatSpecifier, runtimeCore.RuntimeMemory.Heap, 0, true), runtimeCore.RuntimeMemory.Heap); + } return returnSV; } diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs b/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs index 72bb75bd24a..42645747738 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs +++ b/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -655,6 +655,16 @@ public static string FromArraySearchTags { } } + /// + /// Looks up a localized string similar to Format specifier for numeric values, see extended node help for more info. + ///default value: G. (general formatting) + /// + public static string FromObjectPortDataFormatToolTip { + get { + return ResourceManager.GetString("FromObjectPortDataFormatToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Object to be serialized. /// diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx b/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx index dcf7a16c40f..b11d5b442f1 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx +++ b/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx @@ -676,4 +676,8 @@ In Generative Design workflows, this node should be used to control and block th Select Types - + + Format specifier for numeric values, see extended node help for more info. +default value: G + + \ No newline at end of file diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.resx b/src/Libraries/CoreNodeModels/Properties/Resources.resx index aa29f8e175d..ce2eb25174b 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.resx +++ b/src/Libraries/CoreNodeModels/Properties/Resources.resx @@ -676,4 +676,8 @@ In Generative Design workflows, this node should be used to control and block th Select Types - + + Format specifier for numeric values, see extended node help for more info. +default value: G + + \ No newline at end of file diff --git a/src/Libraries/CoreNodeModels/String.cs b/src/Libraries/CoreNodeModels/String.cs index f56750c761d..9db486e94e6 100644 --- a/src/Libraries/CoreNodeModels/String.cs +++ b/src/Libraries/CoreNodeModels/String.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using CoreNodeModels.Properties; using Dynamo.Graph.Nodes; using Newtonsoft.Json; @@ -8,7 +10,7 @@ namespace CoreNodeModels { /// /// Base class to represent a single input string node. It supports - /// partiallied applied function. + /// partially applied function. /// public class ToStringNodeBase : NodeModel { @@ -48,7 +50,7 @@ public override IEnumerable BuildOutputAst(List BuildOutputAst(List inPorts, IEnumerable outPorts) : + base("__ToStringFromObjectAndFormat", inPorts, outPorts) + { + ArgumentLacing = LacingStrategy.Disabled; + if (inPorts?.Count() > 1) + { + inPorts.ElementAt(1).DefaultValue = AstFactory.BuildStringNode("G"); + } + } + + public FormattedStringFromObject() : base("__ToStringFromObjectAndFormat") + { + ArgumentLacing = LacingStrategy.Disabled; + RegisterAllPorts(); + //TODO figure out how to add an inportDefaultValue attribute. + //it's not straightforward because ideally we'd have accesss to the parser. + if (InPorts?.Count() > 1) + { + InPorts.ElementAt(1).DefaultValue = AstFactory.BuildStringNode("G"); + InPorts.ElementAt(1).UsingDefaultValue = true; + } + } + } + [NodeName("String from Object")] [NodeDescription("StringfromObjectDescription", typeof(Resources))] [NodeCategory("Core.String.Actions")] @@ -74,7 +115,6 @@ private FromObject(IEnumerable inPorts, IEnumerable outPor { ArgumentLacing = LacingStrategy.Disabled; } - public FromObject() : base("__ToStringFromObject") { ArgumentLacing = LacingStrategy.Disabled; @@ -84,6 +124,45 @@ public FromObject() : base("__ToStringFromObject") } } + [NodeName("Formatted String from Array")] + [NodeDescription("StringfromArrayDescription", typeof(Resources))] + [NodeCategory("Core.String.Actions")] + [NodeSearchTags("FromArraySearchTags", typeof(Resources))] + [IsDesignScriptCompatible] + [System.Diagnostics.CodeAnalysis.Experimental("NEWNODE_FormattedStringFromArray")] + [InPortNames("object", "formatSpecifier")] + [InPortTypes("var", "string")] + [InPortDescriptions(typeof(Resources), "FromArrayPortDataArrayToolTip", "FromObjectPortDataFormatToolTip")] + [OutPortNames("string")] + [OutPortTypes("string")] + [OutPortDescriptions(typeof(Resources),"FromArrayPortDataResultToolTip")] + public class FormattedStringFromArray : ToStringNodeBase + { + [JsonConstructor] + private FormattedStringFromArray(IEnumerable inPorts, IEnumerable outPorts) : + base("__ToStringFromArrayAndFormat", inPorts, outPorts) + { + ArgumentLacing = LacingStrategy.Disabled; + if (inPorts?.Count() > 1) + { + inPorts.ElementAt(1).DefaultValue = AstFactory.BuildStringNode("G"); + } + } + + public FormattedStringFromArray() : base("__ToStringFromArrayAndFormat") + { + ArgumentLacing = LacingStrategy.Disabled; + RegisterAllPorts(); + //TODO figure out how to add an inportDefaultValue attribute. + //it's not straightforward because ideally we'd have accesss to the parser. + if (InPorts?.Count() > 1) + { + InPorts.ElementAt(1).DefaultValue = AstFactory.BuildStringNode("G"); + InPorts.ElementAt(1).UsingDefaultValue = true; + } + } + } + [NodeName("String from Array")] [NodeDescription("StringfromArrayDescription", typeof(Resources))] [NodeCategory("Core.String.Actions")] diff --git a/src/LibraryViewExtensionWebView2/Packages/LibrarieJS/layoutSpecs.json b/src/LibraryViewExtensionWebView2/Packages/LibrarieJS/layoutSpecs.json index 6b6414a3c5b..3f42eead7ed 100644 --- a/src/LibraryViewExtensionWebView2/Packages/LibrarieJS/layoutSpecs.json +++ b/src/LibraryViewExtensionWebView2/Packages/LibrarieJS/layoutSpecs.json @@ -1311,20 +1311,23 @@ "text": "Generate", "iconUrl": "", "elementType": "group", - "include": [ - { - "path": "Core.String.String from Object" - }, - { - "path": "Core.String.String from Array" - }, - { - "path": "DSCoreNodes.DSCore.String.Concat" - }, - { - "path": "DSCoreNodes.DSCore.String.Join" - } - ], + "include": [ + { + "path": "Core.String.String from Object" + }, + { + "path": "Core.String.String from Array" + }, + { + "path": "DSCoreNodes.DSCore.String.Concat" + }, + { + "path": "DSCoreNodes.DSCore.String.Join" + }, + { + "path": "Core.String" + }, + ], "childElements": [] } ] diff --git a/test/DynamoCoreTests/CodeBlockNodeTests.cs b/test/DynamoCoreTests/CodeBlockNodeTests.cs index 9b9b1f05e85..dc80b92d5f2 100644 --- a/test/DynamoCoreTests/CodeBlockNodeTests.cs +++ b/test/DynamoCoreTests/CodeBlockNodeTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -2475,10 +2475,13 @@ public void TestMethodKeywordCompletionWhenTyping() string code = "im"; var completions = codeCompletionServices.SearchCompletions(code, Guid.Empty); - // Expected 3 completion items - Assert.AreEqual(3, completions.Count()); + Assert.AreEqual(5, completions.Count()); - string[] expected = { "Imperative", "Minimal", "MinimalTracedClass" }; + string[] expected = { "ClassWithExperimentalMethod", + "ExperimentalClass", + "Imperative", + "Minimal", + "MinimalTracedClass" }; var actual = completions.Select(x => x.Text).OrderBy(x => x); Assert.AreEqual(expected, actual); diff --git a/test/DynamoCoreTests/DSEvaluationUnitTestBase.cs b/test/DynamoCoreTests/DSEvaluationUnitTestBase.cs index 02e38cde2ce..2f8e3480d59 100644 --- a/test/DynamoCoreTests/DSEvaluationUnitTestBase.cs +++ b/test/DynamoCoreTests/DSEvaluationUnitTestBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -156,6 +156,11 @@ protected void AssertError(string guid) var node = GetModel().CurrentWorkspace.Nodes.First(n => n.GUID.ToString() == guid); Assert.True(node.IsInErrorState); } + protected void AssertWarning(string guid) + { + var node = GetModel().CurrentWorkspace.Nodes.First(n => n.GUID.ToString() == guid); + Assert.True(node.State is ElementState.Warning || node.State is ElementState.PersistentWarning); + } protected void AssertPreviewValue(string guid, object value) { diff --git a/test/DynamoCoreTests/ExperimentalNodesTests.cs b/test/DynamoCoreTests/ExperimentalNodesTests.cs new file mode 100644 index 00000000000..0bc68ca3051 --- /dev/null +++ b/test/DynamoCoreTests/ExperimentalNodesTests.cs @@ -0,0 +1,37 @@ +using Dynamo.Engine; +using NUnit.Framework; + + +namespace Dynamo.Tests +{ + [TestFixture] + internal class ExperimentalNodesTests + { + [Test] + public void FunctionDescriptorIsMarkedExperimentalByExperimentalPrefsSection() + { + var x = new FunctionDescriptor(new FunctionDescriptorParams + { + Assembly = "ProtoGeometry.dll", + ClassName = "Autodesk.DesignScript.Geometry.PanelSurface", + FunctionName = "somefunc", + Parameters = [], + ReturnType = ProtoCore.TypeSystem.BuildPrimitiveTypeObject(ProtoCore.PrimitiveType.Void), + FunctionType = FunctionType.InstanceMethod, + IsVisibleInLibrary = true, + ReturnKeys = [], + PathManager = null, + IsVarArg = false, + ObsoleteMsg = null, + CanUpdatePeriodically = false, + IsBuiltIn = false, + IsPackageMember = false, + IsLacingDisabled = false, + //even though this is set to false, the function should be marked as experimental + //because the assembly/classname combination is marked as experimental in the experimental prefs section + IsExperimental = false, + }); + Assert.That(x.IsExperimental, Is.True); + } + } +} diff --git a/test/DynamoCoreTests/LibraryTests.cs b/test/DynamoCoreTests/LibraryTests.cs index 24aefe334bd..1bf322b11d2 100644 --- a/test/DynamoCoreTests/LibraryTests.cs +++ b/test/DynamoCoreTests/LibraryTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Xml; @@ -111,6 +111,24 @@ public void TestLoadNoNamespaceClass() } } + [Test] + public void FunctionDescriptorIsMarkedExperimentalByAttr() + { + string libraryPath = "FFITarget.dll"; + if (!libraryServices.IsLibraryLoaded(libraryPath)) + { + libraryServices.ImportLibrary(libraryPath); + Assert.IsTrue(LibraryLoaded); + } + //get our function descriptor that is marked experimental + var functions = libraryServices.GetFunctionGroups(libraryPath).SelectMany(x => x.Functions.Where(x => x.IsExperimental)); + Assert.AreEqual(2,functions.Count()); + + //marked experimental because method is experimental + Assert.AreEqual("ExperimentalMethod", functions.ElementAt(0).FunctionName); + //implicitly marked experimental because owning class is experimental + Assert.AreEqual("Method", functions.ElementAt(1).FunctionName); + } [Test] [Category("UnitTests")] diff --git a/test/DynamoCoreTests/Nodes/StringTests.cs b/test/DynamoCoreTests/Nodes/StringTests.cs index e4db34851b4..7d164f2316a 100644 --- a/test/DynamoCoreTests/Nodes/StringTests.cs +++ b/test/DynamoCoreTests/Nodes/StringTests.cs @@ -2,8 +2,10 @@ using System.IO; using System.Linq; using CoreNodeModels; +using Dynamo.Configuration; using Dynamo.Graph.Nodes; using Dynamo.Graph.Nodes.ZeroTouch; +using Dynamo.Models; using NUnit.Framework; namespace Dynamo.Tests @@ -439,6 +441,37 @@ public void TestStringToNumberNormalInput() AssertPreviewValue("898ee89d-a934-4b43-a051-da3459be329a", 1000); AssertPreviewValue("0afc0a8f-3d8a-4d7c-a2ec-d868cbb29b5f", 123456789); } + [Test] + public void TestStringToNumberWithFormat() + { + string testFilePath = Path.Combine(localDynamoStringTestFolder, "TestNumberToString_normal_numericFormat.dyn"); + CurrentDynamoModel.PreferenceSettings.NumberFormat = "f1"; + RunModel(testFilePath); + var watch0 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch0").FirstOrDefault().GUID.ToString(); + var watch1 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch1").FirstOrDefault().GUID.ToString(); + var watch2 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch2").FirstOrDefault().GUID.ToString(); + var watch3parent = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch3") + .FirstOrDefault().ImediateUpstreamNodes().FirstOrDefault().GUID.ToString(); + var watch3 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch3").FirstOrDefault().GUID.ToString(); + var watch4 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch4").FirstOrDefault().GUID.ToString(); + var watch4parent = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch4") + .FirstOrDefault().ImediateUpstreamNodes().FirstOrDefault().GUID.ToString(); + var watch5 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch5").FirstOrDefault().GUID.ToString(); + var watch6 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch6").FirstOrDefault().GUID.ToString(); + var watch7 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch7").FirstOrDefault().GUID.ToString(); + + AssertPreviewValue(watch0, "123456789"); + AssertPreviewValue(watch1, new string[]{ "123456789.000", "123456789.0", "123456789", "111010110111100110100010101" }); + AssertPreviewValue(watch2, new string[] { "-123456789.000", "-123456789.0", "-123456789", "1111111111111111111111111111111111111000101001000011001011101011" }); + AssertPreviewValue(watch3, new string[] { "3.460", "3.5", "3.46", "Format specifier was invalid." }); + AssertPreviewValue(watch4, new string[] { "-3.460", "-3.5", "-3.46", "Format specifier was invalid." }); + AssertPreviewValue(watch5, new string[] { "5.000", "5.0", "5", "101" }); + AssertPreviewValue(watch6, new string[] { "{key:0.000000000}", "{key:0.000000000}", "{key:0.000000000}", "{key:0.000000000}" }); + AssertPreviewValue(watch7, new string[] { "{key:5}", "{key:5}", "{key:5}", "{key:5}" }); + + AssertWarning(watch3parent); + AssertWarning(watch4parent); + } #endregion @@ -866,6 +899,45 @@ public void TestStringFromArray() RunModel(testFilePath); AssertPreviewValue("c27d9e05-45f7-4aac-8f53-a9e485e0f9c0", "[1,2,3]"); + } + [Test] + public void TestStringFromArrayWithFormat() + { + string testFilePath = Path.Combine(localDynamoStringTestFolder, "TestStringFromArrayPreview_numericformat.dyn"); + + RunModel(testFilePath); + var watch1 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch1").FirstOrDefault().GUID.ToString(); + var watch2 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch2").FirstOrDefault().GUID.ToString(); + var watch3 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch3").FirstOrDefault().GUID.ToString(); + var watch4 = CurrentDynamoModel.CurrentWorkspace.Nodes.Where(x => x.Name.ToLower() == "watch4").FirstOrDefault().GUID.ToString(); + + AssertPreviewValue(watch1, "[1.000000,1.666667,2.333333,3.000000]"); + AssertPreviewValue(watch2, "[1,1.6666666666666667,2.3333333333333335,3]"); + AssertPreviewValue(watch3, new string[] { "[1.0,1.7,2.3,3.0]", "[1,1.6666666666666667,2.3333333333333335,3]", "Format specifier was invalid." }); + AssertPreviewValue(watch4, "Function"); + + } + + [Test] + public void StringFormatNodesAreCurrentlyExperimental() + { +#pragma warning disable NEWNODE_FormattedStringFromObject +#pragma warning disable NEWNODE_FormattedStringFromArray + +#pragma warning disable NM_ISEXPERIMENTAL_GLPYH + + var formatString = new FormattedStringFromObject(); + var formatStringArr = new FormattedStringFromArray(); + Assert.AreEqual(true,formatString.IsExperimental); + Assert.AreEqual(true, formatStringArr.IsExperimental); + Assert.IsTrue(formatString.InPorts[1].DefaultValue.Kind == ProtoCore.AST.AssociativeAST.AstKind.String && formatString.InPorts[1].UsingDefaultValue == true); + Assert.IsTrue(formatStringArr.InPorts[1].DefaultValue.Kind == ProtoCore.AST.AssociativeAST.AstKind.String && formatStringArr.InPorts[1].UsingDefaultValue == true); + +#pragma warning restore NM_ISEXPERIMENTAL_GLPYH +#pragma warning restore NEWNODE_FormattedStringFromObject +#pragma warning restore NEWNODE_FormattedStringFromArray + + } #endregion } diff --git a/test/DynamoCoreWpfTests/NodeViewCustomizationTests.cs b/test/DynamoCoreWpfTests/NodeViewCustomizationTests.cs index 2aaae5a5534..1fd807650d2 100644 --- a/test/DynamoCoreWpfTests/NodeViewCustomizationTests.cs +++ b/test/DynamoCoreWpfTests/NodeViewCustomizationTests.cs @@ -312,9 +312,9 @@ public void WatchImageCoreContainsImage() // Starting from Dynamo 2.13, node view now comes with // images like node icon, lacing image etc - // As of March 2022, we have 7 images per NodeView + // As of Nov 2024, we have 8 images per NodeView // Images are named for ease of use - Assert.AreEqual(7, imgs.Count()); + Assert.AreEqual(8, imgs.Count()); var img = imgs.First(x => x.Name == "DotsImage"); diff --git a/test/Engine/FFITarget/ExperimentalNodes.cs b/test/Engine/FFITarget/ExperimentalNodes.cs new file mode 100644 index 00000000000..585de86ff6f --- /dev/null +++ b/test/Engine/FFITarget/ExperimentalNodes.cs @@ -0,0 +1,24 @@ + +using System.Diagnostics.CodeAnalysis; + + +namespace FFITarget +{ + public static class ClassWithExperimentalMethod + { + [Experimental("FFI_1")] + public static string ExperimentalMethod() + { + return "I am an experimental node!"; + } + } + [Experimental("FFI_2")] + public static class ExperimentalClass + { + + public static string Method() + { + return "my owning class is experimental"; + } + } +} diff --git a/test/Engine/ProtoTest/TD/MultiLangTests/StringTest.cs b/test/Engine/ProtoTest/TD/MultiLangTests/StringTest.cs index 9fcf54a255c..ae2bc905365 100644 --- a/test/Engine/ProtoTest/TD/MultiLangTests/StringTest.cs +++ b/test/Engine/ProtoTest/TD/MultiLangTests/StringTest.cs @@ -567,5 +567,70 @@ public void TestLocalizedStringInCode() thisTest.RunScriptSource(code); thisTest.Verify("x", "中文字符"); } + [Test] + public void TestStringFromArrayFormat() { + String code = + @" +import(""FFITarget.dll""); + a = ClassFunctionality.ClassFunctionality(1); + arr1 = [1,2]; + arr2 = [1.100005,a]; + arr3 = [5,a,1.1]; + b1 = ""a"" + __ToStringFromArrayAndFormat(arr1,""F2""); + b2 = ""a"" + __ToStringFromArrayAndFormat(arr2, ""G3""); + b3 = __ToStringFromArrayAndFormat(arr3, ""B""); + "; + thisTest.RunScriptSource(code); + thisTest.Verify("b1", "a[1.00,2.00]"); + thisTest.Verify("b2", "a[1.1,FFITarget.ClassFunctionality]"); + //TODO note this difference between array and object conversion - if any format specifier is invalid the entire array conversion fails. + //how should this behave? + thisTest.Verify("b3", "Format specifier was invalid." ); + + } + [Test] + public void TestStringFromObjectFormat() + { + String code = + @" +import(""FFITarget.dll""); + a = ClassFunctionality.ClassFunctionality(1); + arr1 = [1,2]; + arr2 = [1.100005,a]; + arr3 = [5,a,1.1]; + b1 = __ToStringFromObjectAndFormat(arr1,""F2""); + b2 = __ToStringFromObjectAndFormat(arr2, ""G3""); + b3 = __ToStringFromObjectAndFormat(arr3, ""B""); + "; + thisTest.RunScriptSource(code); + thisTest.Verify("b1", new string[] { "1.00", "2.00" }); + thisTest.Verify("b2", new string[]{"1.1","FFITarget.ClassFunctionality"}); + thisTest.Verify("b3", new string[] { "101", "FFITarget.ClassFunctionality","Format specifier was invalid." }); + } + [Test] + public void TestStringFromObjectFormat_Prefs1() + { + String code = + @" + b1 = __ToStringFromObjectAndFormat(1.123456789, ""DynamoPreferencesNumberFormat""); + "; + thisTest.RunScriptSource(code); + thisTest.Verify("b1", "1.123"); + } + [Test] + public void TestStringFromObjectFormat_Prefs2() + { + var oldpref = ProtoCore.Mirror.MirrorData.PrecisionFormat; + ProtoCore.Mirror.MirrorData.PrecisionFormat = "F6"; + String code = + @" + b1 = __ToStringFromObjectAndFormat(1.123456789, ""DynamoPreferencesNumberFormat""); + "; + thisTest.RunScriptSource(code); + thisTest.Verify("b1", "1.123457"); + //reset + ProtoCore.Mirror.MirrorData.PrecisionFormat = oldpref; + + } } } diff --git a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs index b171d96b890..2cf6a6d8844 100644 --- a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs +++ b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs @@ -164,7 +164,7 @@ public void ProducesCorrectOutputFromCoreDirectory_preloadedbinaries() FromDirectoryCommand.HandleDocumentationFromDirectory(opts); var generatedFileNames = tempDirectory.GetFiles().Select(x => x.Name); - Assert.AreEqual(754, generatedFileNames.Count()); + Assert.AreEqual(756, generatedFileNames.Count()); } [Test] @@ -225,7 +225,9 @@ public void DictionaryContentIsFoundCorrectlyForCoreNodes() // Arrange var testOutputDirName = "TestMdOutput_CoreNodeModels"; - + //these are new files/nodes so there is no dictionary content fo them. + var filesToSkip = new string[] { "CoreNodeModels.FormattedStringFromObject", "CoreNodeModels.FormattedStringFromArray" }; + var coreNodeModelsDll = Path.Combine(DynamoCoreNodesDir, CORENODEMODELS_DLL_NAME); Assert.That(File.Exists(coreNodeModelsDll)); @@ -250,8 +252,8 @@ public void DictionaryContentIsFoundCorrectlyForCoreNodes() //assert that the generated markdown files all contain an "indepth section" from the dictionary entry, which means //they were all found. - - Assert.True(generatedFileNames.Where(x=>Path.GetExtension(x).Contains("md")).All(x => File.ReadAllText(x).ToLower().Contains("in depth"))); + var generatedFileNamesSubset = generatedFileNames.Where(x => !filesToSkip.Contains(Path.GetFileNameWithoutExtension(x))); + Assert.True(generatedFileNamesSubset.Where(x=>Path.GetExtension(x).Contains("md")).All(x => File.ReadAllText(x).ToLower().Contains("in depth"))); } diff --git a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromArray.md b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromArray.md new file mode 100644 index 00000000000..21be3f4831e --- /dev/null +++ b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromArray.md @@ -0,0 +1,5 @@ +## String from Array And Format - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=3.4.0.6676, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromObject.md b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromObject.md new file mode 100644 index 00000000000..1b39c296071 --- /dev/null +++ b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FormattedStringFromObject.md @@ -0,0 +1,5 @@ +## Formatted String From Object - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=3.4.0.6676, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromArray.md b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromArray.md index 53fe56fd7ab..0b7f7c81296 100644 --- a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromArray.md +++ b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromArray.md @@ -1,5 +1,5 @@ -## String from Array - Documentation -This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.13.0.2212, Culture=neutral, PublicKeyToken=null. +## String From Array - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=3.4.0.6676, Culture=neutral, PublicKeyToken=null. For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes diff --git a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromObject.md b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromObject.md index 62127b5e2a0..b8f0f9e51f5 100644 --- a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromObject.md +++ b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.FromObject.md @@ -1,5 +1,5 @@ -## String from Object - Documentation -This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.13.0.2212, Culture=neutral, PublicKeyToken=null. +## String From Object - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=3.4.0.6676, Culture=neutral, PublicKeyToken=null. For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes diff --git a/test/core/string/TestNumberToString_normal_numericformat.dyn b/test/core/string/TestNumberToString_normal_numericformat.dyn new file mode 100644 index 00000000000..bfc672d6e60 --- /dev/null +++ b/test/core/string/TestNumberToString_normal_numericformat.dyn @@ -0,0 +1,1195 @@ +{ + "Uuid": "3c9d0464-8643-5ffe-96e5-ab1769818209", + "IsCustomNode": false, + "Description": "", + "Name": "TestNumberToString_normal_numericformat", + "ElementResolver": { + "ResolutionMap": { + "DesignScript.Builtin.Dictionary": { + "Key": "DesignScript.Builtin.Dictionary", + "Value": "DesignScriptBuiltin.dll" + } + } + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "CoreNodeModels.Input.DoubleInput, CoreNodeModels", + "NumberType": "Double", + "Id": "b6fbfc57f9a14609912d3824f806f5b8", + "NodeType": "NumberInputNode", + "Inputs": [], + "Outputs": [ + { + "Id": "a5a3fa26f7dc45a39a173e9928d26464", + "Name": "", + "Description": "Double", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Creates a number", + "InputValue": 123456789.0 + }, + { + "ConcreteType": "CoreNodeModels.Input.DoubleInput, CoreNodeModels", + "NumberType": "Double", + "Id": "9b74289ccd7242749bfbb6b56d2774bb", + "NodeType": "NumberInputNode", + "Inputs": [], + "Outputs": [ + { + "Id": "14d00072325d4ff4a55b64a6e794da3a", + "Name": "", + "Description": "Double", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Creates a number", + "InputValue": -123456789.0 + }, + { + "ConcreteType": "CoreNodeModels.Input.DoubleInput, CoreNodeModels", + "NumberType": "Double", + "Id": "075ecd65d9b44489b09041a309f0b794", + "NodeType": "NumberInputNode", + "Inputs": [], + "Outputs": [ + { + "Id": "3c7008e844154852a868d0360bd5531b", + "Name": "", + "Description": "Double", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Creates a number", + "InputValue": 3.46 + }, + { + "ConcreteType": "CoreNodeModels.Input.DoubleInput, CoreNodeModels", + "NumberType": "Double", + "Id": "1ce66222719e414eb4e393544239f4e5", + "NodeType": "NumberInputNode", + "Inputs": [], + "Outputs": [ + { + "Id": "e5afe6443c5e4dc58e7a1fb8b04bb7ad", + "Name": "", + "Description": "Double", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Creates a number", + "InputValue": -3.46 + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "d3fb3523609f456fbc233dcae1d48732", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "286987f4a6ab4f9580be045e75e6d3c2", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "8b2a3051fe5248e0a03e5d53fe56e0d9", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "ae77e756519e43ad9ac1861827601e22", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "422fe9f197a64b36b0f981800a6d3c21", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "8e370b548b2f4075b769fc4c5276af50", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "1ec6ed7b75a64bfcba77b81f82ed8f50", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "6597820795a745789041ff10a3c1c806", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "9774fa5969bc42d490126d1e6b18bc08", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "680caae1a3ea4095803b2493571cce7a", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "cd3452ad72e94a659eed9d147cff3554", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "6cb36d659e7a4c9791373741e1a07ec3", + "Name": "useNumericFormat", + "Description": "should the numeric precision format be used when converting doubles (disabled)", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "b7a7cd5475b14f069395f5fd4f760779", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "8b3258a0ac584bf5a3261ca66aaa788d", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "7d5f8dd21d0f493da25b9002273d5e80", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "832846b8799d4fe7ae1ac6c9901608cc", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "5e4770f0e3cc417f92776a8b8b83b533", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "b45e2569af7049288b2b0aa558d3a7c3", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "{\"key\":\"0.000000000\"};" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "662c8c937a80459b971650b87a6fcfe4", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "bd621135843f4e32b20128fabb5b349a", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "[\"F3\",\"F1\",\"G\",\"B\"];" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "02c1b3e721dd4941ac15d8461b154a49", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "2c2b4228f1f74ccc9ab191dbcddeef2b", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "3f9edc8af38c40468c872736e7b6dbdb", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "a74956c410ce41b7b43a195dad0e38b5", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "eb1a9bcb42934dc293bfc3350ef15a85", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "{\"key\":\"5\"};" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "f776aa328bd043ee8ec082b49bffe6a3", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "a9b1075f9ebb426694446b6f97308228", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "28bc476b868f4346aacf08498000189f", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values (disabled)", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "484311c3e33d48208bfe70baabef9539", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "3a45200d74bb4ece97d0fb1bb03ec973", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "8c04eff2dbe74b5bbe8e23b5878a1372", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "77435ecf09c347308760f8d9514afd03", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "e0e5a49813ef41e8bbdcaf9844fd6e86", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "2a84b826723841daabe17488bfa4696e", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "5;" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "9c16d38e96ad477dab6fad80d8f28f74", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "065e1a4cbceb41f09a22e802fc9dbe15", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "47f0dc27e828410cbaa454c9c29d0e67", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "1b705b6125b5443195d274ecac8cf387", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "2451b0dca8e54fa98c17fbb6f5aa9c80", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "63692e1c01524a77a4e90dcb64e7ea77", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "1d6f9331f32d43fc97b50d78fab08250", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "3f24ba400c0946f3b530b795f7b07be8", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "c8801f22da1943cab36bd03806cc20cd", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "bb5682d9c8e44bcc92b842ef0d62d3a1", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "7b2cac575b6143b5bc7f3fd275240764", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "dd1cde67a44643578a4f79bbffcec458", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "1bbe9fbe696945ab801c0428693a34c1", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "fa02fe5e367b41a286926a6177e390cd", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "1bdd311b836d48afa8eb87b67e9f7127", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "763a0f6364f24fc4ab60eb421975b941", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "2194d136e214459b8d04f11e5d39f0db", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "4833cd518b874c648878cda9793b7bfe", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "52f0dff47f29434b8709fb228a2e2e51", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "069559e4e385493980c709b0861e8186", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.Input.DoubleInput, CoreNodeModels", + "NumberType": "Double", + "Id": "275e748e781c45d8895214258a51ab90", + "NodeType": "NumberInputNode", + "Inputs": [], + "Outputs": [ + { + "Id": "487ea80b7ca247de898094a2839611e3", + "Name": "", + "Description": "Double", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Creates a number", + "InputValue": 123456789.0 + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromObject, CoreNodeModels", + "Id": "df29ded8e49c4755be88d57e4bda23d9", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "bb7a79b378b549c6aede58d93dc2eb37", + "Name": "object", + "Description": "Object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "6bfb6b40654845b8bf33602a6d179536", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "4140df13525c4d9bb934c8fb6d46a328", + "Name": "string", + "Description": "String representation of the object", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an object to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 184.0, + "WatchHeight": 102.0, + "Id": "92884c6c303641299316c83273ce718c", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "019f22c1b40844b3a7bbc3a551be6310", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "4022383166d94960b5b0e19c43a36148", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 200.0, + "WatchHeight": 200.0, + "Id": "cd8ed092d8a04f3e8a0137d70c3833dd", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "914170b3ab1842cba4f5bb72498f5db5", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "0e62fb306b7e43b2bdcc1a7619a71b9e", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + } + ], + "Connectors": [ + { + "Start": "a5a3fa26f7dc45a39a173e9928d26464", + "End": "065e1a4cbceb41f09a22e802fc9dbe15", + "Id": "05d4a850182c442a92250b00a02f92f5", + "IsHidden": "False" + }, + { + "Start": "14d00072325d4ff4a55b64a6e794da3a", + "End": "63692e1c01524a77a4e90dcb64e7ea77", + "Id": "3f607494975b415f8397960344a70028", + "IsHidden": "False" + }, + { + "Start": "3c7008e844154852a868d0360bd5531b", + "End": "bb5682d9c8e44bcc92b842ef0d62d3a1", + "Id": "09ae3ab31ba44679b93a86f677a6a870", + "IsHidden": "False" + }, + { + "Start": "e5afe6443c5e4dc58e7a1fb8b04bb7ad", + "End": "fa02fe5e367b41a286926a6177e390cd", + "Id": "aa6e857c034340a8a5748027b15c19cd", + "IsHidden": "False" + }, + { + "Start": "b7a7cd5475b14f069395f5fd4f760779", + "End": "7d5f8dd21d0f493da25b9002273d5e80", + "Id": "3192964ec1f745c584ceab0aabcb501c", + "IsHidden": "False" + }, + { + "Start": "b45e2569af7049288b2b0aa558d3a7c3", + "End": "cd3452ad72e94a659eed9d147cff3554", + "Id": "108c352a91fd4048935715e2e3e59b94", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "28bc476b868f4346aacf08498000189f", + "Id": "91c74477d6564065956c24a0a61daad5", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "6cb36d659e7a4c9791373741e1a07ec3", + "Id": "80a067f1cf8a4985988b16b31dd1dbb1", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "47f0dc27e828410cbaa454c9c29d0e67", + "Id": "e09be04861044084b200f58bb9ff4e0c", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "1d6f9331f32d43fc97b50d78fab08250", + "Id": "739924ee07ab4156b85f39253b4985af", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "7b2cac575b6143b5bc7f3fd275240764", + "Id": "0a57f5cb0e204fdda7664165b2653abc", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "1bdd311b836d48afa8eb87b67e9f7127", + "Id": "ae0a23488ea549b8a612b307a5c77a16", + "IsHidden": "False" + }, + { + "Start": "bd621135843f4e32b20128fabb5b349a", + "End": "52f0dff47f29434b8709fb228a2e2e51", + "Id": "3fbd3973e22048d9bfad8450520d8aa9", + "IsHidden": "False" + }, + { + "Start": "eb1a9bcb42934dc293bfc3350ef15a85", + "End": "a9b1075f9ebb426694446b6f97308228", + "Id": "85a1759a56cd40f9a754dc38d5b92b61", + "IsHidden": "False" + }, + { + "Start": "484311c3e33d48208bfe70baabef9539", + "End": "8c04eff2dbe74b5bbe8e23b5878a1372", + "Id": "a97a202dcb524a98ae46476c819c8448", + "IsHidden": "False" + }, + { + "Start": "2a84b826723841daabe17488bfa4696e", + "End": "4833cd518b874c648878cda9793b7bfe", + "Id": "b004d9b62ce247f592ec9c86dff2d1a3", + "IsHidden": "False" + }, + { + "Start": "1b705b6125b5443195d274ecac8cf387", + "End": "286987f4a6ab4f9580be045e75e6d3c2", + "Id": "9ebcc1f2b85d4d9581ae33d6e01c9835", + "IsHidden": "False" + }, + { + "Start": "3f24ba400c0946f3b530b795f7b07be8", + "End": "422fe9f197a64b36b0f981800a6d3c21", + "Id": "3e1ff4c8f2484d4bb657f39ce3acdf76", + "IsHidden": "False" + }, + { + "Start": "dd1cde67a44643578a4f79bbffcec458", + "End": "6597820795a745789041ff10a3c1c806", + "Id": "a1ca444d97c94e869e4b236277258c2e", + "IsHidden": "False" + }, + { + "Start": "763a0f6364f24fc4ab60eb421975b941", + "End": "2c2b4228f1f74ccc9ab191dbcddeef2b", + "Id": "cf7beef7064841f2afd7abe19eda19e3", + "IsHidden": "False" + }, + { + "Start": "069559e4e385493980c709b0861e8186", + "End": "914170b3ab1842cba4f5bb72498f5db5", + "Id": "fa9c716ff76d4162a2fb4c45bf6aad00", + "IsHidden": "False" + }, + { + "Start": "487ea80b7ca247de898094a2839611e3", + "End": "bb7a79b378b549c6aede58d93dc2eb37", + "Id": "2a1fe23f82fb4f2d83975ffe29491012", + "IsHidden": "False" + }, + { + "Start": "4140df13525c4d9bb934c8fb6d46a328", + "End": "019f22c1b40844b3a7bbc3a551be6310", + "Id": "2a24a7679ba44adda65ee4bf0534d6c9", + "IsHidden": "False" + } + ], + "Dependencies": [], + "NodeLibraryDependencies": [], + "EnableLegacyPolyCurveBehavior": true, + "Thumbnail": "", + "GraphDocumentationURL": null, + "ExtensionWorkspaceData": [ + { + "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", + "Name": "Properties", + "Version": "3.0", + "Data": {} + } + ], + "Author": "None provided", + "Linting": { + "activeLinter": "None", + "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", + "warningCount": 0, + "errorCount": 0 + }, + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "3.4.0.6676", + "RunType": "Manual", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "_Background Preview", + "EyeX": -17.0, + "EyeY": 24.0, + "EyeZ": 50.0, + "LookX": 12.0, + "LookY": -13.0, + "LookZ": -58.0, + "UpX": 0.0, + "UpY": 1.0, + "UpZ": 0.0 + }, + "ConnectorPins": [], + "NodeViews": [ + { + "Id": "b6fbfc57f9a14609912d3824f806f5b8", + "Name": "Number", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 115.67426105668028, + "Y": -287.8456424997603 + }, + { + "Id": "9b74289ccd7242749bfbb6b56d2774bb", + "Name": "Number", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 64.33938073241075, + "Y": 39.70634987252754 + }, + { + "Id": "075ecd65d9b44489b09041a309f0b794", + "Name": "Number", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 115.67426105668028, + "Y": 623.7068471196142 + }, + { + "Id": "1ce66222719e414eb4e393544239f4e5", + "Name": "Number", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 52.367205920489596, + "Y": 907.8542090962671 + }, + { + "Id": "d3fb3523609f456fbc233dcae1d48732", + "Name": "Watch1", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 999.6928641254308, + "Y": -54.5580923127701 + }, + { + "Id": "ae77e756519e43ad9ac1861827601e22", + "Name": "Watch2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 998.39733153422, + "Y": 246.587092661364 + }, + { + "Id": "1ec6ed7b75a64bfcba77b81f82ed8f50", + "Name": "Watch3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 969.5559999591637, + "Y": 567.4236591317275 + }, + { + "Id": "680caae1a3ea4095803b2493571cce7a", + "Name": "String from Object2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 721.1166494505987, + "Y": 1605.2830355613005 + }, + { + "Id": "8b3258a0ac584bf5a3261ca66aaa788d", + "Name": "Watch6", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 1040.1166494505987, + "Y": 1605.2830355613005 + }, + { + "Id": "5e4770f0e3cc417f92776a8b8b83b533", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 365.5710819435785, + "Y": 1605.1105355613006 + }, + { + "Id": "662c8c937a80459b971650b87a6fcfe4", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": -475.8980754364303, + "Y": 754.2873402247715 + }, + { + "Id": "02c1b3e721dd4941ac15d8461b154a49", + "Name": "Watch4", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 969.5559999591637, + "Y": 880.4236591317275 + }, + { + "Id": "a74956c410ce41b7b43a195dad0e38b5", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 353.82061073362206, + "Y": 1846.07157949955 + }, + { + "Id": "f776aa328bd043ee8ec082b49bffe6a3", + "Name": "String from Object2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 710.8206107336221, + "Y": 1846.2440794995498 + }, + { + "Id": "3a45200d74bb4ece97d0fb1bb03ec973", + "Name": "Watch7", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 1032.8006474657432, + "Y": 1928.7051347432157 + }, + { + "Id": "e0e5a49813ef41e8bbdcaf9844fd6e86", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": -81.75799888567963, + "Y": 1303.8630016714806 + }, + { + "Id": "9c16d38e96ad477dab6fad80d8f28f74", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 424.4515027871015, + "Y": -295.59369971031424 + }, + { + "Id": "2451b0dca8e54fa98c17fbb6f5aa9c80", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 388.1005679987211, + "Y": 0.4483476842422647 + }, + { + "Id": "c8801f22da1943cab36bd03806cc20cd", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 416.8217740245268, + "Y": 442.56509594756284 + }, + { + "Id": "1bbe9fbe696945ab801c0428693a34c1", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 449.6756726974987, + "Y": 894.8451762576958 + }, + { + "Id": "2194d136e214459b8d04f11e5d39f0db", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 511.71836089332555, + "Y": 1299.6245343442215 + }, + { + "Id": "275e748e781c45d8895214258a51ab90", + "Name": "Number", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 100.54811428273808, + "Y": -505.73875497749736 + }, + { + "Id": "df29ded8e49c4755be88d57e4bda23d9", + "Name": "String from Object And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 409.3253560131593, + "Y": -513.4868121880513 + }, + { + "Id": "92884c6c303641299316c83273ce718c", + "Name": "Watch0", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 884.798673288619, + "Y": -518.8107682252431 + }, + { + "Id": "cd8ed092d8a04f3e8a0137d70c3833dd", + "Name": "Watch5", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 983.0477947579423, + "Y": 1253.668472804629 + } + ], + "Annotations": [], + "X": -169.52963948937793, + "Y": -578.7067270043049, + "Zoom": 0.6459837027060035 + } +} \ No newline at end of file diff --git a/test/core/string/TestStringFromArrayPreview_numericformat.dyn b/test/core/string/TestStringFromArrayPreview_numericformat.dyn new file mode 100644 index 00000000000..bfdad40e514 --- /dev/null +++ b/test/core/string/TestStringFromArrayPreview_numericformat.dyn @@ -0,0 +1,526 @@ +{ + "Uuid": "45732322-5b7c-4527-a7ff-2181708d3c04", + "IsCustomNode": false, + "Description": null, + "Name": "TestFormattedStringFromArrayPreview_numericformat", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "CoreNodeModels.FromArray, CoreNodeModels", + "Id": "c27d9e0545f74aac8f53a9e485e0f9c0", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "cc0b8d6aed9043de91272235e6ab2e50", + "Name": "arr", + "Description": "The array of object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "5144a5cf722c4b3da19b0708be143915", + "Name": "str", + "Description": "String representation of the array", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an array to a string representation" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "012e48cbf12d4277a4ee77c990ec7a68", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "480593de671c4fd0a5aba280e2065a8b", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "1..3..#4;" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 298.0, + "WatchHeight": 38.0, + "Id": "4b6c7485022c4593ad15d280b5e99e8f", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "cc5f61300a9a40b79d66da1579621edc", + "Name": "", + "Description": "Node to evaluate.", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "f0db4981f30a468bbb33fb9d80199b95", + "Name": "", + "Description": "Watch contents.", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "b9352a46c1634e65a64bd8062e2a6102", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "9eebab252bf0454c826a3fb48016ec02", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "[\"F1\",\"G\",\"B\"];" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromArray, CoreNodeModels", + "Id": "74f97ab047fa49eaaa8d77271ecb87e0", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "39204eddb5214b8784ef4d0a66415a2e", + "Name": "array", + "Description": "The array of object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "a93226abb9014f848ced0e08e546b696", + "Name": "useNumericFormat", + "Description": "should the numeric precision format be used when converting doubles (disabled)", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "9bb562665ed04284adf767976c17b2d2", + "Name": "string", + "Description": "String representation of the array", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an array to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 80.0, + "WatchHeight": 38.0, + "Id": "c198ae603bba4cc0a7ef17c3444f8156", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "b389674f395a4746b32d7583d975e743", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "929d7e76504340349292c5c432724364", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 298.0, + "WatchHeight": 38.0, + "Id": "a28fa354113640eab04af51930aaeca2", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "44b3628a3c864218b68753a9552537b3", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "f1307dd499c7413587db852a6a66fa42", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.Watch, CoreNodeModels", + "WatchWidth": 419.0, + "WatchHeight": 136.0, + "Id": "f690cbe74f4c45e7917e742af16219ad", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "0a76d7cfd50044868dc5996ae88c325f", + "Name": "", + "Description": "Node to show output from", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "05280f24000e45ada5b1e986d72423e4", + "Name": "", + "Description": "Node output", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Visualizes a node's output" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromArray, CoreNodeModels", + "Id": "96271f95253f4524a95db2d7400c4f52", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "67584c6e3687412ca4c3fca661a85911", + "Name": "array", + "Description": "The array of object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "56f6b5874d3d4474b9efc9266735b5e4", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "2b5df70c7ac34313813f29f47326e817", + "Name": "string", + "Description": "String representation of the array", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an array to a string representation" + }, + { + "ConcreteType": "CoreNodeModels.FormattedStringFromArray, CoreNodeModels", + "Id": "24cdee3feeb04e09bcad4b0078581347", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "1823631337ab41a58bc0b24670d65ae2", + "Name": "array", + "Description": "The array of object to be serialized", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "ea6ed5138ef74182b621508230aaf896", + "Name": "formatSpecifier", + "Description": "format specifier for numeric values", + "UsingDefaultValue": true, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "2fac04be2c194c2cabf862a710cda54a", + "Name": "string", + "Description": "String representation of the array", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Converts an array to a string representation" + } + ], + "Connectors": [ + { + "Start": "5144a5cf722c4b3da19b0708be143915", + "End": "cc5f61300a9a40b79d66da1579621edc", + "Id": "0c116fd047bc42d4a799794b440ed7f5", + "IsHidden": "False" + }, + { + "Start": "480593de671c4fd0a5aba280e2065a8b", + "End": "cc0b8d6aed9043de91272235e6ab2e50", + "Id": "e1f6a2d64a1547dfb384016d5be86549", + "IsHidden": "False" + }, + { + "Start": "480593de671c4fd0a5aba280e2065a8b", + "End": "39204eddb5214b8784ef4d0a66415a2e", + "Id": "6d4611a0e5914a12be3763132720005c", + "IsHidden": "False" + }, + { + "Start": "480593de671c4fd0a5aba280e2065a8b", + "End": "67584c6e3687412ca4c3fca661a85911", + "Id": "4a27e6486b994d3a83828b2339bbfd80", + "IsHidden": "False" + }, + { + "Start": "480593de671c4fd0a5aba280e2065a8b", + "End": "1823631337ab41a58bc0b24670d65ae2", + "Id": "58753234ed1f47d49702260243e689c9", + "IsHidden": "False" + }, + { + "Start": "9eebab252bf0454c826a3fb48016ec02", + "End": "ea6ed5138ef74182b621508230aaf896", + "Id": "938d461eafdd4ef9806ee339969e12f6", + "IsHidden": "False" + }, + { + "Start": "9bb562665ed04284adf767976c17b2d2", + "End": "b389674f395a4746b32d7583d975e743", + "Id": "319e84e8bc714198802388fda8aebca2", + "IsHidden": "False" + }, + { + "Start": "2b5df70c7ac34313813f29f47326e817", + "End": "44b3628a3c864218b68753a9552537b3", + "Id": "2dd8f3f0b65a43f8b2ac4836724943a1", + "IsHidden": "False" + }, + { + "Start": "2fac04be2c194c2cabf862a710cda54a", + "End": "0a76d7cfd50044868dc5996ae88c325f", + "Id": "2ff00e8d99de423195bc927856b1d761", + "IsHidden": "False" + } + ], + "Dependencies": [], + "NodeLibraryDependencies": [], + "EnableLegacyPolyCurveBehavior": true, + "Thumbnail": "", + "GraphDocumentationURL": null, + "ExtensionWorkspaceData": [ + { + "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", + "Name": "Properties", + "Version": "3.0", + "Data": {} + } + ], + "Author": "None provided", + "Linting": { + "activeLinter": "None", + "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", + "warningCount": 0, + "errorCount": 0 + }, + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "3.4.0.6676", + "RunType": "Automatic", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "_Background Preview", + "EyeX": -17.0, + "EyeY": 24.0, + "EyeZ": 50.0, + "LookX": 12.0, + "LookY": -13.0, + "LookZ": -58.0, + "UpX": 0.0, + "UpY": 1.0, + "UpZ": 0.0 + }, + "ConnectorPins": [], + "NodeViews": [ + { + "Id": "c27d9e0545f74aac8f53a9e485e0f9c0", + "Name": "String from Array", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 329.83615590858915, + "Y": 264.44863102728914 + }, + { + "Id": "012e48cbf12d4277a4ee77c990ec7a68", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": -3.2945482059884625, + "Y": 259.7251911171454 + }, + { + "Id": "4b6c7485022c4593ad15d280b5e99e8f", + "Name": "watch1", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 592.9054158830944, + "Y": 268.0759595281854 + }, + { + "Id": "b9352a46c1634e65a64bd8062e2a6102", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": -129.38907825891982, + "Y": 682.3632653107057 + }, + { + "Id": "74f97ab047fa49eaaa8d77271ecb87e0", + "Name": "String from Array2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 327.4697339717835, + "Y": 881.6712841125085 + }, + { + "Id": "c198ae603bba4cc0a7ef17c3444f8156", + "Name": "watch4", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 611.7258533738277, + "Y": 902.9404607916114 + }, + { + "Id": "a28fa354113640eab04af51930aaeca2", + "Name": "Watch2", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 620.7629312816283, + "Y": 407.74862501298634 + }, + { + "Id": "f690cbe74f4c45e7917e742af16219ad", + "Name": "Watch3", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 692.0084730824933, + "Y": 652.511871701363 + }, + { + "Id": "96271f95253f4524a95db2d7400c4f52", + "Name": "String from Array And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 260.57759376051, + "Y": 410.86538545620147 + }, + { + "Id": "24cdee3feeb04e09bcad4b0078581347", + "Name": "String from Array And Format", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 305.38333347175626, + "Y": 656.9108064199578 + } + ], + "Annotations": [], + "X": 5.4580877421955165, + "Y": -256.1436648704163, + "Zoom": 0.6181807073903587 + } +} \ No newline at end of file