From c24fb7eaa0b7f55100efec17033ceae6f481b197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= <88777268+bjoernsteinhagen@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:44:22 +0300 Subject: [PATCH 1/6] fix(rhino): remove control characters from revit families for rhino layer names (#1195) --- .../HostApp/RhinoLayerBaker.cs | 8 +++- .../HostApp/RhinoUtils.cs | 38 +++++++++++++------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs index c4f9fbb48..80a76e455 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs @@ -123,6 +123,12 @@ public class RhinoLayerBaker : TraversalContextUnpacker } var cleanNewLayerName = RhinoUtils.CleanLayerName(collection.name); + + if (!ModelComponent.IsValidComponentName(cleanNewLayerName)) + { + throw new SpeckleException($"Layer name '{currentLayerName}' is not valid"); + } + Layer newLayer = new() { Name = cleanNewLayerName, ParentLayerId = previousLayer?.Id ?? Guid.Empty }; // set material @@ -150,7 +156,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker int index = currentDocument.Layers.Add(newLayer); if (index == -1) { - throw new SpeckleException($"Could not create layer '{currentLayerName}'."); + throw new SpeckleException($"Could not create layer '{currentLayerName}'"); } _hostLayerCache.Add(currentLayerName, index); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoUtils.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoUtils.cs index c88c4460f..dd9063a97 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoUtils.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoUtils.cs @@ -1,27 +1,41 @@ +using System.Text; + namespace Speckle.Connectors.Rhino.HostApp; public static class RhinoUtils { - public static string CleanBlockDefinitionName(string str) - { - return ReplaceChars(str, @"\/", "_"); - } + private static readonly HashSet s_skipChars = ['[', ']', '(', ')', '{', '}']; + private static readonly HashSet s_replaceWithHyphen = [':', ';']; + + public static string CleanBlockDefinitionName(string str) => str.Replace('/', '_').Replace('\\', '_'); // Cleans up layer names to be "rhino" proof. Note this can be improved, as "()[] and {}" are illegal only at the start. // https://docs.mcneel.com/rhino/6/help/en-us/index.htm#information/namingconventions.htm?Highlight=naming public static string CleanLayerName(string str) { - str = ReplaceChars(str, @"[](){}", ""); - return ReplaceChars(str, @":;", "-"); - } + var sb = new StringBuilder(str.Length); - private static string ReplaceChars(string str, string invalidChars, string replaceString) - { - foreach (char c in invalidChars) + foreach (char c in str) { - str = str.Replace(c.ToString(), replaceString); + if (char.IsControl(c)) + { + continue; // skip control characters (shoutout cnx-2809) + } + + if (s_skipChars.Contains(c)) + { + continue; // skip brackets + } + + if (s_replaceWithHyphen.Contains(c)) + { + sb.Append('-'); + continue; + } + + sb.Append(c); } - return str; + return sb.ToString(); } } From c533d0922a4ee19d6aa8e63cedbc21ad29094449 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:54:29 +0000 Subject: [PATCH 2/6] chore(deps): bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pr.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 2fdc2c12d..29580698d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e668efe3a..da1caa474 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: file_version: ${{ steps.set-version.outputs.file_version }} steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 From 93ede98135e0ac6ff6e472ce34e4aa0b8436a014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= <88777268+bjoernsteinhagen@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:43:13 +0300 Subject: [PATCH 3/6] fix(grasshopper): `CastFrom` with long (#1197) * fix: cast from with long * chore: be loud about future casting fails --- .../Components/Objects/ExpandSpeckleProperties.cs | 4 +--- .../Parameters/SpecklePropertyGoo.cs | 3 +++ .../Parameters/SpecklePropertyGroupGoo.cs | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Objects/ExpandSpeckleProperties.cs b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Objects/ExpandSpeckleProperties.cs index 97bc3e51d..7d1cace40 100644 --- a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Objects/ExpandSpeckleProperties.cs +++ b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Objects/ExpandSpeckleProperties.cs @@ -27,8 +27,7 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon protected override Bitmap Icon => Resources.speckle_properties_expand; public override GH_Exposure Exposure => GH_Exposure.secondary; - protected override void RegisterInputParams(GH_InputParamManager pManager) - { + protected override void RegisterInputParams(GH_InputParamManager pManager) => pManager.AddParameter( new SpecklePropertyGroupParam(), "Properties", @@ -36,7 +35,6 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon "Speckle Properties to expand", GH_ParamAccess.item ); - } protected override void RegisterOutputParams(GH_OutputParamManager pManager) { } diff --git a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGoo.cs b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGoo.cs index a067b6272..106533394 100644 --- a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGoo.cs +++ b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGoo.cs @@ -79,6 +79,9 @@ public class SpecklePropertyGoo : GH_Goo, ISpecklePropertyGoo case int i: Value = i; return true; + case long l: + Value = l; + return true; case string s: Value = s; return true; diff --git a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGroupGoo.cs b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGroupGoo.cs index c42037925..f1accff41 100644 --- a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGroupGoo.cs +++ b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Parameters/SpecklePropertyGroupGoo.cs @@ -161,7 +161,12 @@ public partial class SpecklePropertyGroupGoo : GH_Goo Date: Fri, 28 Nov 2025 14:37:26 +0300 Subject: [PATCH 4/6] refactor(grasshopper): renaming component naming for automatic loading (#1200) --- .../Components/Operations/Receive/ReceiveAsyncComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Operations/Receive/ReceiveAsyncComponent.cs b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Operations/Receive/ReceiveAsyncComponent.cs index 7f21a70b4..8cf6318d6 100644 --- a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Operations/Receive/ReceiveAsyncComponent.cs +++ b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Operations/Receive/ReceiveAsyncComponent.cs @@ -130,7 +130,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent { var autoReceiveMi = Menu_AppendItem( menu, - "Load automatically", + "Load new versions automatically", (s, e) => { AutoReceive = !AutoReceive; From a7b3ae878023a8f809a56993d2ba5634dc213433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= <88777268+bjoernsteinhagen@users.noreply.github.com> Date: Fri, 28 Nov 2025 14:41:47 +0300 Subject: [PATCH 5/6] refactor(grasshopper): appending "with Token" to "Speckle Model URL" component name (#1201) --- .../Components/Dev/TokenUrlComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Dev/TokenUrlComponent.cs b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Dev/TokenUrlComponent.cs index 0d022f078..36491def7 100644 --- a/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Dev/TokenUrlComponent.cs +++ b/Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Dev/TokenUrlComponent.cs @@ -15,7 +15,7 @@ public class TokenUrlComponent : GH_Component { public TokenUrlComponent() : base( - "Speckle Model URL", + "Speckle Model URL with Token", "URL", "Create a Speckle model link using URL and developer token", ComponentCategories.PRIMARY_RIBBON, From 6f72402b76bee5f58ddfaa7bb6558bc3a81e1312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= <88777268+bjoernsteinhagen@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:25:37 +0300 Subject: [PATCH 6/6] fix(revit): rebar and area reinforcement transforms with reference points (#1202) * fix: rebar transforms * fix: area reinf. --- .../Helpers/DisplayValueExtractor.cs | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs index 5d55f2d49..aecc04a14 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/DisplayValueExtractor.cs @@ -74,15 +74,18 @@ public sealed class DisplayValueExtractor } return areaDisplay; - // NOTE: this is only for Rebar and not AreaReinforcement, RebarInSystem - // AreaReinforcement and RebarInSystem pass through GetGeometryDisplayValue which get DisplayValues as per hostApp - // Rebar elements need special handling as get_Geometry() doesn't work properly - // We either represent them as centerlines or as solids based on settings + // Rebar: get_Geometry() returns null, use GetTransformedCenterlineCurves/GetFullGeometryForView + apply reference point transform case DB.Structure.Rebar rebar: return _converterSettings.Current.SendRebarsAsVolumetric ? GetRebarVolumetricDisplayValue(rebar) : GetRebarCenterlineDisplayValue(rebar); + // AreaReinforcement/PathReinforcement get_Geometry() returns curves in document coordinates + // unlike Rebar which needs reference point transform applied, these are already correct + case DB.Structure.AreaReinforcement: + case DB.Structure.PathReinforcement: + return GetAreaReinforcementDisplayValue(element); + // handle specific types of objects with multiple parts or children // curtain and stacked walls should have their display values in their children case DB.Wall wall: @@ -107,7 +110,7 @@ public sealed class DisplayValueExtractor using DB.Transform? compoundTransform = localToDocument is not null && documentToWorld is not null ? documentToWorld.Multiply(localToDocument) - : localToDocument; // don't want to accidentally dispose of the ReferencePointTransform + : localToDocument; DB.Transform? localToWorld = compoundTransform ?? documentToWorld; @@ -423,7 +426,7 @@ public sealed class DisplayValueExtractor return false; // exit fast on a potential hot path } - DB.GraphicsStyle? bjk = null; // ask ogu why this variable is named like this + DB.GraphicsStyle? bjk; // ask ogu why this variable is named like this if (!_graphicStyleCache.ContainsKey(geomObj.GraphicsStyleId.ToString().NotNull())) { @@ -547,8 +550,9 @@ public sealed class DisplayValueExtractor if (geometryElements != null) { + DB.Transform? documentToWorld = _converterSettings.Current.ReferencePointTransform?.Inverse; SortGeometry(rebar, collections, geometryElements, null); - return ProcessGeometryCollections(rebar, collections, null); + return ProcessGeometryCollections(rebar, collections, documentToWorld); } // Return empty list if no geometry is found - imo not critical @@ -589,16 +593,40 @@ public sealed class DisplayValueExtractor ) ); } + DB.Transform? documentToWorld = _converterSettings.Current.ReferencePointTransform?.Inverse; List displayValue = new(); foreach (var curve in curves) { - displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve))); + if (documentToWorld is not null) + { + using var transformedCurve = curve.CreateTransformed(documentToWorld); + displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(transformedCurve))); + } + else + { + displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve))); + } } return displayValue; } + /// + /// Gets display value for AreaReinforcement and PathReinforcement. + /// + /// + /// These elements' get_Geometry() returns curves already in document coordinates. + /// Unlike Rebar.GetTransformedCenterlineCurves() which requires reference point transform, + /// these curves should not be transformed - they're already in the correct space. + /// + private List GetAreaReinforcementDisplayValue(DB.Element element) + { + var collections = GetSortedGeometryFromElement(element, null, null); + // pass null for transform - curves are already in correct document coordinates + return ProcessGeometryCollections(element, collections, null); + } + /// /// Represents sorted collections of different geometry types extracted from an element. /// Used to pass multiple geometry collections as a single parameter to improve code readability