BIM Link Example Implement BimApi Geometry - idea-statica/ideastatica-public GitHub Wiki

For the RSTAB BimLink, the following BimApi files relate to Model Geometry:

BimApi Geometry Interfaces inherit from the IIdeaPersistentObject to enable syncing and updating of geometry. The persistent object interface requires the implementation of a persistence Token property.

/// <summary>
/// An object that can be recreated at any point by its persistence <see cref="Token"/>.
/// </summary>
public interface IIdeaPersistentObject : IIdeaObject
{
    /// <summary>
    /// Persistence token. Holds data neccessery to recreated the object in future.
    /// </summary>
    IIdeaPersistenceToken Token { get; }
}

Node Implementation

Looking at the RstabNode.cs contents which inherets from the IIdeaNode interface:

  • A No property is provided to enable a numbering of nodes separately from other elements.
  • As Rstab provides a number of ways to define the coordinate position of a node, therefore the import session is being provided to the node constructor to tell it which way RSTAB is defining the coordinate system. This would not likely be required in other circumstances. *We also provide some additional methods (GetNodePosition(), GetCartesianPosition()) to convert the coordinate position of the Node Point to a cartesian position which is required by IOM.
  • The GetData() method simply retrieves the RSTAB Node from the API which is stored on the _modelDataProvider.
using Dlubal.RSTAB8;
using IdeaRstabPlugin.Providers;
using IdeaStatiCa.BimApi;
using IdeaStatiCa.BimImporter.Persistence;
using IdeaStatiCa.Plugin;
using System;

namespace IdeaRstabPlugin.BimApi
{
    internal class RstabNode : IIdeaNode
    {
        private readonly static IPluginLogger _logger = LoggerProvider.GetLogger("bim.rstab.bimapi");

        /// Position of the node in cartesian coordinates.
        public IdeaVector3D Vector => GetNodePosition();

        /// Unique object id.
        public string Id => "node-" + Name;

        /// Name of the node.
        public string Name => No.ToString();

        public IIdeaPersistenceToken Token { get; }

        public int No { get; }

        private readonly IImportSession _importSession;
        private readonly IModelDataProvider _modelDataProvider;

        public RstabNode(IImportSession importSession, IModelDataProvider modelDataProvider, int nodeNo)
        {
            _importSession = importSession;
            _modelDataProvider = modelDataProvider;

            No = nodeNo;
            Token = new PersistenceToken(TokenObjectType.Node, No);

            _logger.LogDebug($"Created {nameof(RstabNode)} with id {Id}");
        }

        private Node GetData()
        {
            return _modelDataProvider.GetNode(No);
        }

        private IdeaVector3D GetNodePosition()
        {
            IdeaVector3D pos = GetCartesianPosition();

            if (!_importSession.IsGCSOrientedUpwards)
            {
                // RSTAB rotates the Y axis to keep the coordinate system right handed.
                return new IdeaVector3D(pos.X, -pos.Y, -pos.Z);
            }

            return pos;
        }

        /// Cartesian, XCylindrical, YCylindrical, ZCylindrical, or Polar.</exception>
        private IdeaVector3D GetCartesianPosition()
        {
            double r, theta, phi;

            Node nodeData = GetData();

            switch (nodeData.CS)
            {
                case CoordinateSystemType.Cartesian:
                    return new IdeaVector3D(nodeData.X, nodeData.Y, nodeData.Z);

                case CoordinateSystemType.XCylindrical:
                    r = nodeData.Y;
                    theta = nodeData.Z;
                    return new IdeaVector3D(nodeData.X, Math.Cos(theta) * r, Math.Sin(theta) * r);

                case CoordinateSystemType.YCylindrical:
                    r = nodeData.X;
                    theta = nodeData.Z;
                    return new IdeaVector3D(Math.Sin(theta) * r, nodeData.Y, Math.Cos(theta) * r);

                case CoordinateSystemType.ZCylindrical:
                    r = nodeData.X;
                    theta = nodeData.Y;
                    return new IdeaVector3D(Math.Cos(theta) * r, Math.Sin(theta) * r, nodeData.Z);

                case CoordinateSystemType.Polar:
                    r = nodeData.X;
                    theta = nodeData.Y;
                    phi = nodeData.Z;
                    return new IdeaVector3D(
                        Math.Sin(phi) * Math.Cos(theta) * r,
                        Math.Sin(phi) * Math.Sin(theta) * r,
                        Math.Cos(phi) * r);

                default:
                    throw new NotImplementedException($"Unsupported coordinate system '{nodeData.CS}'.");
            }
        }
    }
}

Segment3D Implementation

Looking at the RstabSegment3D.cs contents which inherets from the IIdeaElement1D Interface:

  • The Segment3d class contains the local coordinate system of elements and members and therefore is provided at the constructor.
  • The objectFactory is also passed to the constructor along with the start and end node numbers. These numbers correspond to the No property on the RstabNode.
  • The RstabLineSegment3D is created at the time of the Element1D creation.

It is worth noting that Arc geometry is not supported here, if so a RstabArcSegment3D inheriting from IIdeaArcSegement3D would also be required to be implemented.

using IdeaRS.OpenModel.Geometry3D;
using IdeaRstabPlugin.Factories;
using IdeaStatiCa.BimApi;
using IdeaStatiCa.Plugin;

namespace IdeaRstabPlugin.BimApi
{
    /// <inheritdoc cref="IIdeaLineSegment3D"/>
    internal class RstabLineSegment3D : IIdeaLineSegment3D
    {
        private readonly static IPluginLogger _logger = LoggerProvider.GetLogger("bim.rstab.bimapi");

        public IIdeaNode StartNode => _objectFactory.GetNode(_startNodeNo);

        public IIdeaNode EndNode => _objectFactory.GetNode(_endNodeNo);

        public CoordSystem LocalCoordinateSystem { get; }

        public string Id => "segment-" + Name;

        public string Name { get; }

        private readonly IObjectFactory _objectFactory;
        private readonly int _startNodeNo;
        private readonly int _endNodeNo;

        public RstabLineSegment3D(IObjectFactory objectFactory, CoordSystem cs, int startNodeNo, int endNodeNo)
        {
            _objectFactory = objectFactory;

            _startNodeNo = startNodeNo;
            _endNodeNo = endNodeNo;

            if (startNodeNo < endNodeNo)
            {
                Name = $"line-{startNodeNo}-{endNodeNo}";
            }
            else
            {
                Name = $"line-{endNodeNo}-{startNodeNo}";
            }

            LocalCoordinateSystem = cs;

            _logger.LogDebug($"Created {nameof(RstabLineSegment3D)} with id {Id}");
        }
    }
}

Element1D Implementation

Looking at the RstabElemnt1D.cs contents which inherits from the IIdeaElement1D Interface:

  • The object factory is passed to the constructor along with the start and end node numbers and start and end cross sections. These numbers correspond to the No property on the RstabNode.
using IdeaRS.OpenModel.Geometry3D;
using IdeaRstabPlugin.Factories;
using IdeaStatiCa.BimApi;
using IdeaStatiCa.BimApi.Results;
using IdeaStatiCa.Plugin;
using System.Collections.Generic;
using System.Linq;

namespace IdeaRstabPlugin.BimApi
{
    /// <inheritdoc cref="IIdeaElement1D"/>
    internal class RstabElement1D : IIdeaElement1D
    {
        private readonly static IPluginLogger _logger = LoggerProvider.GetLogger("bim.rstab.bimapi");

        public IIdeaCrossSection StartCrossSection => _objectFactory.GetCrossSection(_startCssNo);

        public IIdeaCrossSection EndCrossSection
        {
            get
            {
                if (_endCssNo == 0)
                {
                    return StartCrossSection;
                }
                else
                {
                    return _objectFactory.GetCrossSection(_endCssNo);
                }
            }
        }

        public IdeaVector3D EccentricityBegin { get; set; } = new IdeaVector3D(0.0, 0.0, 0.0);

        public IdeaVector3D EccentricityEnd { get; set; } = new IdeaVector3D(0.0, 0.0, 0.0);

        public double RotationRx { get; }

        public IIdeaSegment3D Segment { get; }

        public string Id => "element-" + Name;

        public string Name { get; }

        private readonly IObjectFactory _objectFactory;
        private readonly int _startCssNo;
        private readonly int _endCssNo;

        public RstabElement1D(IObjectFactory objectFactory, CoordSystem coordSystem, int startNodeNo, int endNodeNo,
            int startCrossSectionNo, int endCrossSectionNo)
        {
            _objectFactory = objectFactory;
            _startCssNo = startCrossSectionNo;
            _endCssNo = endCrossSectionNo;

            Segment = new RstabLineSegment3D(objectFactory, coordSystem, startNodeNo, endNodeNo);
            RotationRx = 0.0;

            if (startNodeNo < endNodeNo)
            {
                Name = $"line-{startNodeNo}-{endNodeNo}";
            }
            else
            {
                Name = $"line-{endNodeNo}-{startNodeNo}";
            }

            _logger.LogDebug($"Created {nameof(RstabElement1D)} with id {Id}");
        }

        public IEnumerable<IIdeaResult> GetResults()
        {
            return Enumerable.Empty<IIdeaResult>();
        }
    }
}

RstabMember1D Implementation

Looking at the RstabMember1D.cs contents which inherit from the IIdeaMember1D Interface:

  • The object factory is passed to the constructor along with the start and end node numbers and start and end cross-sections. These numbers correspond to the No property on the RstabNode.
  • The GetData() method simply retrieves the RSTAB Member from the API which is stored on the _modelDataProvider.
  • The _elementFactory provides methods that create elements that make up the members.
  • The member No serves as the distinct integer of the member within the list of Model Members.
using Dlubal.RSTAB8;
using IdeaRS.OpenModel.Model;
using IdeaRstabPlugin.Factories;
using IdeaRstabPlugin.Providers;
using IdeaStatiCa.BimApi;
using IdeaStatiCa.BimApi.Results;
using IdeaStatiCa.BimImporter.Persistence;
using IdeaStatiCa.Plugin;
using System.Collections.Generic;

namespace IdeaRstabPlugin.BimApi
{
    /// <inheritdoc cref="IIdeaMember1D"/>
    internal class RstabMember : IIdeaMember1D
    {
        private readonly static IPluginLogger _logger = LoggerProvider.GetLogger("bim.rstab.bimapi");

        public Member1DType Type => GetMemberType();

        public List<IIdeaElement1D> Elements => _elementFactory.GetElements(_memberNo);

        public string Id => "member-" + Name;

        public string Name => No.ToString();

        public int No => GetData().No;

        public IIdeaPersistenceToken Token => new PersistenceToken(TokenObjectType.Member, No);

        private readonly IObjectFactory _objectFactory;
        private readonly IResultsFactory _resultsFactory;
        private readonly IModelDataProvider _modelDataProvider;
        private readonly IElementFactory _elementFactory;
        private readonly int _memberNo;

        public RstabMember(IObjectFactory objectFactory,
            IModelDataProvider modelDataProvider,
            IResultsFactory resultsFactory,
            IElementFactory elementFactory,
            int memberNo)
        {
            _objectFactory = objectFactory;
            _modelDataProvider = modelDataProvider;
            _resultsFactory = resultsFactory;
            _elementFactory = elementFactory;
            _memberNo = memberNo;

            _logger.LogDebug($"Created {nameof(RstabMember)} with id {Id}");
        }

        public IEnumerable<IIdeaResult> GetResults()
        {
            return _resultsFactory.GetResultsForMember(GetData());
        }

        private Member1DType GetMemberType()
        {
            switch (GetData().Type)
            {
                case MemberType.Buckling:
                case MemberType.Compression:
                    return Member1DType.Column;

                //case MemberType.RibType:
                //  return Member1DType.Rib;

                case MemberType.Truss:
                    return Member1DType.Truss;

                default:
                    return Member1DType.Beam;
            }
        }

        private Member GetData() => _modelDataProvider.GetMember(_memberNo);
    }
}

Remarks on BimApi Geometry Implementation

⚠️ **GitHub.com Fallback** ⚠️