diff --git a/doc/distrib/xml/en-US/DSCoreNodes.xml b/doc/distrib/xml/en-US/DSCoreNodes.xml index 348b8be3932..c2359910a61 100644 --- a/doc/distrib/xml/en-US/DSCoreNodes.xml +++ b/doc/distrib/xml/en-US/DSCoreNodes.xml @@ -396,7 +396,7 @@ A class representing a DataType supported by Dynamo - + A class representing a DataType supported by Dynamo @@ -411,6 +411,12 @@ An optional Name to override the Type name (`Number` instead of `long`) + + + Wire-format $typeid (e.g. "autodesk.math:point3d-1.0.0"). + Null for primitive types that don't use $typeid serialization. + + The hierarchical level to be displayed in the UI diff --git a/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs b/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs new file mode 100644 index 00000000000..da4953338aa --- /dev/null +++ b/src/DynamoCore/Graph/Nodes/IValueSchemaProvider.cs @@ -0,0 +1,24 @@ +namespace Dynamo.Graph.Nodes +{ + /// + /// Implemented by NodeModels that can describe their value type + /// for external consumers (DynamoPlayer, MCP, etc.). + /// Returns a canonical Forge Data Schema type identifier and list context. + /// + public interface IValueSchemaProvider + { + /// + /// Type identifier matching the Player ValueSchema.TypeId wire format. + /// For geometry/complex types: Forge Data Schema URN (e.g. "autodesk.math:point3d-1.0.0"). + /// For primitives: ForgeDataSchemaType values (e.g. "Float64", "String", "Bool", "Int64"). + /// Returns a non-null type identifier. If the type is not yet determined, + /// implementations should return their existing non-null fallback value. + /// + string ValueTypeId { get; } + + /// + /// Whether the value is a list (array) of the declared type. + /// + bool IsListValue { get; } + } +} diff --git a/src/DynamoCore/PublicAPI.Unshipped.txt b/src/DynamoCore/PublicAPI.Unshipped.txt index 475a38b3c81..fd19c94b62b 100644 --- a/src/DynamoCore/PublicAPI.Unshipped.txt +++ b/src/DynamoCore/PublicAPI.Unshipped.txt @@ -1,3 +1,6 @@ +Dynamo.Graph.Nodes.IValueSchemaProvider +Dynamo.Graph.Nodes.IValueSchemaProvider.IsListValue.get -> bool +Dynamo.Graph.Nodes.IValueSchemaProvider.ValueTypeId.get -> string Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool Dynamo.Models.DynamoModel.DefaultStartConfiguration.EnableUnTrustedLocationsNotifications.set -> void Dynamo.Models.DynamoModel.IStartConfiguration.EnableUnTrustedLocationsNotifications.get -> bool diff --git a/src/Libraries/CoreNodeModels/DefineData.cs b/src/Libraries/CoreNodeModels/DefineData.cs index 1703a43f901..375bf6b7f7c 100644 --- a/src/Libraries/CoreNodeModels/DefineData.cs +++ b/src/Libraries/CoreNodeModels/DefineData.cs @@ -25,8 +25,25 @@ namespace CoreNodeModels [OutPortDescriptions(typeof(Properties.Resources), nameof(Properties.Resources.DefineDataOutputTooltip))] [IsDesignScriptCompatible] [AlsoKnownAs("Data.DefineData")] - public class DefineData : DSDropDownBase + public class DefineData : DSDropDownBase, IValueSchemaProvider { + /// + [JsonIgnore] + public string ValueTypeId + { + get + { + if (SelectedIndex < 0 || SelectedIndex >= Items.Count) + return SelectedString; + + return (Items[SelectedIndex].Item as Data.DataNodeDynamoType)?.TypeId ?? SelectedString; + } + } + + /// + [JsonIgnore] + public bool IsListValue => IsList; + private bool isAutoMode = true; // default start with auto-detect 'on' private bool isList; private string displayValue = Properties.Resources.DefineDataDisplayValueMessage; diff --git a/src/Libraries/CoreNodes/Data.cs b/src/Libraries/CoreNodes/Data.cs index 959586acacc..ac5933199b5 100644 --- a/src/Libraries/CoreNodes/Data.cs +++ b/src/Libraries/CoreNodes/Data.cs @@ -534,7 +534,7 @@ public static Dictionary Remember([ArbitraryDimensionArrayImport /// /// A class representing a DataType supported by Dynamo /// - internal class DataNodeDynamoType(Type type, string name = null) + internal class DataNodeDynamoType(Type type, string name = null, string typeId = null) { /// /// The underlying Type @@ -545,6 +545,11 @@ internal class DataNodeDynamoType(Type type, string name = null) /// public string Name { get; private set; } = name ?? type.Name; /// + /// Wire-format $typeid (e.g. "autodesk.math:point3d-1.0.0"). + /// Null for primitive types that don't use $typeid serialization. + /// + public string TypeId { get; private set; } = typeId; + /// /// The hierarchical level to be displayed in the UI /// public int Level { get; private set; } = 0; @@ -557,8 +562,8 @@ internal class DataNodeDynamoType(Type type, string name = null) /// public DataNodeDynamoType Parent { get; private set; } - public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null) - : this(type, name) + public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string name = null, DataNodeDynamoType parent = null, string typeId = null) + : this(type, name, typeId) { Level = level; IsLastChild = isLastChild; @@ -577,53 +582,55 @@ public DataNodeDynamoType(Type type, int level, bool isLastChild = false, string /// static Data() { - var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null); - var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve); - var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve); // polygon is subtype of polyCurve - var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve); // rectangle is subtype of polygon - var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null); - var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid); // cone is subtype of solid - var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone); // cylinder is subtype of cone - var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid); // cuboid is subtype of solid - var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid); // sphere is subtype of solid - - var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null); + var curve = new DataNodeDynamoType(typeof(Curve), 0, false, null, null, "dynamo.geometry:sab-1.0.0"); + var polyCurve = new DataNodeDynamoType(typeof(PolyCurve), 1, false, null, curve, "autodesk.geometry.curve:compositecurve-1.0.0"); + var polygon = new DataNodeDynamoType(typeof(Polygon), 2, false, null, polyCurve, "autodesk.geometry.curve:polyline-1.0.0"); + var rectangle = new DataNodeDynamoType(typeof(Autodesk.DesignScript.Geometry.Rectangle), 3, true, null, polyCurve, "dynamo.geometry:rectangle-1.0.0"); + var solid = new DataNodeDynamoType(typeof(Solid), 0, false, null, null, "dynamo.geometry:sab-1.0.0"); + var cone = new DataNodeDynamoType(typeof(Cone), 1, false, null, solid, "dynamo.geometry:cone-1.0.0"); + var cylinder = new DataNodeDynamoType(typeof(Cylinder), 2, false, null, cone, "autodesk.geometry.surface:cylinder-2.0.0"); + var cuboid = new DataNodeDynamoType(typeof(Cuboid), 1, false, null, solid, "dynamo.geometry:cuboid-1.0.0"); + var sphere = new DataNodeDynamoType(typeof(Sphere), 1, true, null, solid, "autodesk.geometry.surface:sphere-1.0.0"); + + var surface = new DataNodeDynamoType(typeof(Surface), 0, false, null, null, "dynamo.geometry:sab-1.0.0"); var typeList = new List { - new(typeof(bool)), - new(typeof(BoundingBox)), - new(typeof(CoordinateSystem)), + new(typeof(bool), typeId: "Bool"), + new(typeof(BoundingBox), typeId: "autodesk.geometry:boundingbox3d-1.0.0"), + new(typeof(CoordinateSystem), typeId: "autodesk.math:matrix44d-1.0.0"), + new(typeof(DSCore.Color), typeId: "dynamo.graphics:color-1.0.0"), curve, - new(typeof(Arc), 1, false, null, curve), - new(typeof(Circle), 1, false, null, curve), - new(typeof(Ellipse), 1, false, null, curve), - new(typeof(EllipseArc), 1, false, null, curve), - new(typeof(Helix), 1, false, null, curve), - new(typeof(Line), 1, false, null, curve), - new(typeof(NurbsCurve), 1, false, null, curve), + new(typeof(Arc), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"), + new(typeof(Circle), 1, false, null, curve, "autodesk.geometry.curve:circle-1.0.0"), + new(typeof(Ellipse), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"), + new(typeof(EllipseArc), 1, false, null, curve, "autodesk.geometry.curve:ellipse-1.0.0"), + new(typeof(Helix), 1, false, null, curve, "dynamo.geometry:sab-1.0.0"), + new(typeof(System.Drawing.Bitmap), "Image", typeId: "dynamo.graphics:png-1.0.0"), + new(typeof(Line), 1, false, null, curve, "autodesk.geometry.curve:line-1.0.0"), + new(typeof(NurbsCurve), 1, false, null, curve, "autodesk.geometry.curve:bcurve-1.0.0"), polyCurve, polygon, rectangle, - new(typeof(System.DateTime)), - new(typeof(double), "Number"), - new(typeof(long), "Integer"), - new(typeof(Location)), - new(typeof(Mesh)), - new(typeof(Plane)), - new(typeof(Autodesk.DesignScript.Geometry.Point)), + new(typeof(System.DateTime), typeId: "DateTime"), + new(typeof(double), "Number", typeId: "Float64"), + new(typeof(long), "Integer", typeId: "Int64"), + new(typeof(Location), typeId: "dynamo.data:location-1.0.0"), + new(typeof(Mesh), typeId: "dynamo.geometry:mesh-1.0.0"), + new(typeof(Plane), typeId: "autodesk.geometry.surface:plane-1.0.0"), + new(typeof(Autodesk.DesignScript.Geometry.Point), typeId: "autodesk.math:point3d-1.0.0"), solid, cone, cylinder, cuboid, sphere, - new(typeof(string)), + new(typeof(string), typeId: "String"), surface, - new(typeof(NurbsSurface), 1, false, null, surface), - new(typeof(PolySurface), 1, true, null, surface), - new(typeof(System.TimeSpan)), - new(typeof(UV)), - new(typeof(Vector)) + new(typeof(NurbsSurface), 1, false, null, surface, "autodesk.geometry.curve:bsurface-1.0.0"), + new(typeof(PolySurface), 1, true, null, surface, "dynamo.geometry:sab-1.0.0"), + new(typeof(System.TimeSpan), typeId: "TimeSpan"), + new(typeof(UV), typeId: "autodesk.math:uv-1.0.0"), + new(typeof(Vector), typeId: "autodesk.math:vector3d-1.0.0") }; DataNodeDynamoTypeList = new ReadOnlyCollection(typeList); diff --git a/test/DynamoCoreTests/Nodes/DefineDataTests.cs b/test/DynamoCoreTests/Nodes/DefineDataTests.cs new file mode 100644 index 00000000000..39725ed329d --- /dev/null +++ b/test/DynamoCoreTests/Nodes/DefineDataTests.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +using CoreNodeModels; +using NUnit.Framework; + +namespace Dynamo.Tests.Nodes +{ + [TestFixture] + internal class DefineDataTests : DynamoModelTestBase + { + protected override void GetLibrariesToPreload(List libraries) + { + libraries.Add("DSCoreNodes.dll"); + base.GetLibrariesToPreload(libraries); + } + + [Test] + [Category("UnitTests")] + public void ValueSchemaProviderReturnsTypeId() + { + var node = new DefineData(); + CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false); + + // Find a type with a non-null TypeId for testing + var pointType = DSCore.Data.DataNodeDynamoTypeList.First(t => t.Type == typeof(Autodesk.DesignScript.Geometry.Point)); + Assert.IsNotNull(pointType.TypeId, "Point type should have a valid TypeId"); + + // Set the node to Point type + node.SelectedIndex = DSCore.Data.DataNodeDynamoTypeList.IndexOf(pointType); + + Assert.AreEqual(pointType.TypeId, node.ValueTypeId); + Assert.IsFalse(node.IsListValue); + } + + [Test] + [Category("UnitTests")] + public void ValueTypeIdSafeWhenNoSelection() + { + var node = new DefineData(); + Assert.DoesNotThrow(() => { var _ = node.ValueTypeId; }); + Assert.AreEqual(node.SelectedString, node.ValueTypeId); + } + + [Test] + [Category("UnitTests")] + public void ValueTypeIdReturnsPrimitiveTypeId() + { + var node = new DefineData(); + CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false); + + var boolType = DSCore.Data.DataNodeDynamoTypeList.First(t => t.Type == typeof(bool)); + node.SelectedIndex = DSCore.Data.DataNodeDynamoTypeList.IndexOf(boolType); + + Assert.AreEqual("Bool", node.ValueTypeId); + } + + [Test] + [Category("UnitTests")] + public void ValueTypeIdReturnsFloat64ForNumber() + { + var node = new DefineData(); + CurrentDynamoModel.CurrentWorkspace.AddAndRegisterNode(node, false); + + var numberType = DSCore.Data.DataNodeDynamoTypeList.First(t => t.Type == typeof(double)); + node.SelectedIndex = DSCore.Data.DataNodeDynamoTypeList.IndexOf(numberType); + + Assert.AreEqual("Float64", node.ValueTypeId); + } + } +}