[CE U13 C#] Measure Volume Elements

I want to measure a simple volume (solid) element but with the code below, CurveVector is null. As it looks, CurveVector is not right for that element.

public static decimal getValueAsDecimal(Bentley.DgnPlatformNET.Elements.Element ElementIn, MSElementProperties elemPropIn)
      {
         decimal decValue = 0;
         CurveVector c = CurvePathQuery.ElementToCurveVector(ElementIn);


         if (c == null)
            return 0;

         switch (elemPropIn)
         {
            case MSElementProperties.Area:
               {
                  double dArea;
                  DPoint3d pt;
                  DVector3d normal;

                  c.CentroidNormalArea(out pt, out normal, out dArea);
                  decValue = (decimal)dArea;
                  break;
               }
               ...
                return decValue;
      }

  • CurveVector is not right fora simple volume (solid) element

    Correct! A CurveVector contains lines, arcs etc. It may be closed, meaning that it describes an area feature.

    I want to measure a simple volume (solid) element

    Class SolidPrimitiveQuery looks like a candidate for that job, but I don't see a method to get the solid's volume.

    The C++ MicroStationAPI contains class ISolidPrimitive, which has method ComputePrincipalMoments (double &volume, DVec3dR centroid, RotMatrixR axes, DVec3dR momentxyz).  It also has ComputeSecondMomentAreaProducts (that gets the solid surface area but not its volume) which is available in C#.  I conclude that ComputePrincipalMoments's absence from the .NET API is an oversight.  File a service request (SR) with Bentley Systems to have it included.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jean-Pierre,

    as Jon wrote, it seems Volume property is not implemented yet in Solid element query. But there is a workaround: To obtain an element volume, you can query element EC property. Example of the code is (ignore element id, used to test the code functionality):

    DgnModel activeModel = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
    
    ulong id = 833;
    ElementId elementId = new ElementId(ref id);
    Element element = activeModel.FindElementById(elementId);
    
    DgnECInstanceCollection collection = DgnECManager.Manager.GetElementProperties(element, ECQueryProcessFlags.SearchAllClasses);
    
    foreach (IDgnECInstance instance in collection)
    {
        IECPropertyValue result = instance.GetPropertyValue("Volume");
        if (null != result)
        {
            double volume;
            if (result.TryGetDoubleValue(out volume))
            {
                ModelInfo modelInfo = activeModel.GetModelInfo();
                double cubicScale = System.Math.Pow(modelInfo.UorPerMaster, 3);
                string ecClassName = instance.ClassDefinition.Name;
                MessageCenter.Instance.StatusMessage = "EC class: " + ecClassName + "; Volume: " + (volume / cubicScale).ToString();
            }
        }
    }

    A problem with EC properties is that they depend on their names and it's not defined anywhere that all 3D elements have "Volume" property defined. But I think it's true in this case. I tried it with primitive box (BoxSolicElement class) and parametric solid (SmartFeatureContainer class) and it works fine.

    With regards,

      Jan

    Answer Verified By: Jean-Pierre Hundhausen 

  • Example of the code

    Good work-around!

    When formatting measurements, there are several formatter classes provided by C# and C++.  In your example, the VolumeFormatter will be useful. It takes care of dimensionality and UORs for you.

     
    Regards, Jon Summers
    LA Solutions

  • A problem with EC properties is that they depend on their names and it's not defined anywhere that all 3D elements have "Volume" property defined.

    Is there a list of the names, especially for other types? SurfaceArea did worked.
    Overall this worked fine and is better than my workaround to go over the Interop Element.

    Mit freundlichen Grüßen / Best regards
    Jean-Pierre Hundhausen

    |  AB_DATE Engineering  Software   |  ab-date.de  |

  • Is there a list of the names, especially for other types? SurfaceArea did worked.

    No ... yes ;-)

    No: There is not any official list of EC properties for a particular element type(s).

    Yes: You can find this information yourself easily, even by two different ways (but both require some knowledge and detective work ;-):

    • In ECschemas folder in MicroStation there are EC schemas definition for all MicroStation types. You can open the files (especially DgnElementSchema.01.00.ecschema.xml) and check how EC class, representing MicroStation element, is defined. The mapping is often (but not always) straightforward: Line element is described by LineElement class, on the other hand parametric solids are SmartFeatureContainer).
    • Use "ecx items dump" key-in to save the model content to xml file. In this file, (nearly) everything from the model is converted into EC format, so you can find an element you are interested in and to check what EC properties are used to describe it.
    Overall this worked fine and is better than my workaround to go over the Interop Element.

    In fact it's quite standard now in MicroStation: EC description has become Lingua Franca internally, because it allows to describe different data structures in standardized format.

    With regards,

      Jan

  • Is there a list of the names, especially for other types?

    Look at the EC schemas you will find in C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\ECSchemas.  For example, SurfaceArea is defined in BaseElementSchema as a property of classes MstnSurface and MstnVolume.

     
    Regards, Jon Summers
    LA Solutions

  • In your example, the VolumeFormatter will be useful. It takes care of dimensionality and UORs for you.

    Yes, I agree, these classes makes value formatting simpler:

    DgnECInstanceCollection collection = DgnECManager.Manager.GetElementProperties(element, ECQueryProcessFlags.SearchAllClasses);
    
    foreach (IDgnECInstance instance in collection)
    {
        MessageCenter.Instance.StatusMessage = "EC class " + instance.ClassDefinition.Name + " evaluated";
    
        IECPropertyValue result = instance.GetPropertyValue("Volume");
        if (null != result)
        {
            double volume;
    
            if (result.TryGetDoubleValue(out volume))
            {
                VolumeFormatter formatter = new VolumeFormatter(activeModel);
                formatter.ShowUnitLabel = true;
    
                MessageCenter.Instance.StatusMessage = "Volume: " + formatter.ToString(volume);
            }
        }
    }

    There are more way how to make this "proof of concept" code better, e.g. to do not search all classes and iterate all results, but to search only for classes relevant for "Volume" information. Often it will be not big difference, but when working with parametric solids, every input geometry and every operation is represented by own EC class, so the collection can contains tens of classes, where only "root" SmartFeatureCollection provides "Volume" property.

    Regards,

      Jan