Updating Z values of a linestring element's vertices points. Applying the changes is not showing.

Currently am using Microstation CONNECT (Update 10).  Visual Studio 2015 Professional, c#, updating a DGN file.  

The concept of my tool is to loop of a collection of selected elements (Linestrings) and compare each line string with one another to see if there are any intersecting points.  My method is to take a line, and compare it with each other line (1 at a time), and then compare the points of each line to see if there are any matching (X,Y) values, both X and Y values need to match.  If there is a match between two linestring elements, then I will continue to check if the Z values are different, if they are different then I will take the average of the two Z values and assign the new averaged value to each point of the lines.  

My current problem is, after I assign the Z values and then call either Element.Rewrite() or Element.Redraw, then nothing will happen.  Could anyone assist me in "Applying" the values so the change in Z values actually shows up?  

I understand that my method of finding intersecting points is not necessarily the most efficient, but that is something I look to optimize later.  I current concern is why the Z values will not update after I set a new z-value.

I have previously referred to this forum here:

https://communities.bentley.com/products/programming/microstation_programming/f/microstation-programming---forum/96252/how-to-modify-elevation-z-values-of-linestring-to-match-another-linestring

Thanks ahead of time all,

Cheers

c# code below:

Main()
{
            B.ElementEnumerator ee = BMI.Utilities.ComApp.ActiveModelReference.GetSelectedElements();
            B.ElementEnumerator eeInnerLoop = BMI.Utilities.ComApp.ActiveModelReference.GetSelectedElements();
            

            while (ee.MoveNext())
            {
                try
                {
                    var CurrentBreakLine = ee.Current;
                    if (CurrentBreakLine.Type.ToString().ToLower() == "linestring")
                    {
                        while (eeInnerLoop.MoveNext())
                        {
                            var comparingBreakLine = eeInnerLoop.Current;
                            if (CurrentBreakLine.ID != comparingBreakLine.ID)
                            {
                                CompareTwoBreakLines(CurrentBreakLine, comparingBreakLine);
                            }                           
                        }
                        eeInnerLoop.Reset();
                    }
                }
                catch(Exception e)
                {
                    MessageBox.Show(e.Message);
                }                                                
            }
}

public void CompareTwoBreakLines(B.Element currentline, B.Element NextLine)
        {
            var currentLineElement = currentline.AsLineElement();
            var nextLineElement = NextLine.AsLineElement();

            int currentVerticeCount = currentLineElement.VerticesCount;
            int nextlineVerticeCount = nextLineElement.VerticesCount;

            B.Point3d[] currentLinePoints = currentLineElement.GetVertices();
            B.Point3d[] nextLinePoints = nextLineElement.GetVertices();

            int outerIndex;
            int innerIndex;

            for (outerIndex = 0; outerIndex <= currentVerticeCount - 1; outerIndex++)
            {
                double currentXValue = currentLinePoints[outerIndex].X;

                for (innerIndex = 0; innerIndex <= nextlineVerticeCount - 1; innerIndex++)
                {
                    double nextLinesCurrentXValue = nextLinePoints[innerIndex].X;
                    if (currentXValue == nextLinesCurrentXValue)
                    {
                        double currentline_YValue = currentLinePoints[outerIndex].Y;
                        double nextLines_YValue = nextLinePoints[innerIndex].Y;
                        if (currentline_YValue == nextLines_YValue)
                        {
                            //Find averages to set Z values between the two current Z Values
                            double z1 = currentLinePoints[outerIndex].Z;
                            double z2 = nextLinePoints[innerIndex].Z;
                            if (z1 != z2)
                            {
                                double newZValue = (z1 + z2) / 2;
                                currentLinePoints[outerIndex].Z = newZValue;
                                currentline.Rewrite();
                                ////currentline.Redraw();  I have tried redraw here as well instead of rewrite
                                nextLinePoints[innerIndex].Z = newZValue;
                                NextLine.Rewrite();
                                ////NextLine.Redraw();  I have tried redraw here as well instead of rewrite
                            }
                        }
                    }
                }
            }
        }

  • // Make a copy of current line element's vertices
    B.Point3d[] currentLinePoints = currentLineElement.GetVertices();
    ...
    // Modify copy of vertices
    currentLinePoints[outerIndex].Z = newZValue;
    // Rewrite element — which has its original vertices
    currentline.Rewrite();

    Does that explain the problem?

    A common idiom when modifying a DGN element is to extract its relevant data (e.g. vertices), then create a new element with the modified data.  Pass the original element as a template so that the new element inherits everything that you don't care about...

    LineStringElement newLine = new LineStringElement (dgnModel, originalEl, currentLinePoints);
    // Add new element
    // Delete original element
    

     
    Regards, Jon Summers
    LA Solutions

  • Hi,

    Currently am using Microstation CONNECT (Update 10).

    When writing code for CONNECT Edition, often it's better to use new NET API (DgnPlatformNET, MStnPlatformNET...) that provides faster, more flexbile and better structured API, closer to native C++. On the other hand, it's probably simpler to learn Interop (VBA) API and when performance or special functionality is not required, to use Interop is more straightforward way.

    My current problem is, after I assign the Z values

    I do not analyze your code too much, but my assumption is that you do not modify the line string Z value, but Z value of copy of the line string vertex. You should be aware of how structs behave in C# and consequences when a struct is returned from method.

    Could anyone assist me in "Applying" the values so the change in Z values actually shows up?  

    Interop API is ready to do it:

    Point3d vertex = line.get_Vertex(index);
    vertex.Z = 123456.789;
    line.ModifyVertex(index, ref vertex);

    or Element.Redraw

    There is no reason to use Redraw method from V8 XM Edition (only in a few specific situations).

    I understand that my method of finding intersecting points is not necessarily the most efficient

    I think there are several serious mistakes in your code (just personal opinion):

    while (ee.MoveNext())
    {
        try
        {
        ...

    There is no reason to catch exception here (especially in a loop). What is an intention and expected functionality?

     

    if (CurrentBreakLine.Type.ToString().ToLower() == "linestring")

    Oh, no! The only functionality of this is to make your code 100x slower. Are you aware of of string comparison performance cost?

    There are (at least) two ways how to check an element type:

    if (ee.Current.IsLineElement())
    
    or
    
    if (MsdElementType.LineString == ee.Current.Type)

    Personally in V8i code I used the second one.

     

    double z1 = currentLinePoints[outerIndex].Z;
    double z2 = nextLinePoints[innerIndex].Z;
    if (z1 != z2)

    Oops ... it will not work as expected in some situations. It's still better than z1 == z2 that is serious coding failure, but still not right. When working with double, it's nonse to test for equality and also inequality test can be unsafe providing wrong results. For an explanation (which is one from core knowledge when working with CAD and GIS data) search for "comparing double values". Quick answer: You always have to define using what precision (tolerance) the test should be done.

    To compare whether 2 points are identical, API provides special method:

    bool Point3dEqualTolerance([In] ref Point3d Vector1, [In] ref Point3d Vector2, [In] double Tolerance);

    I am not aware of a method to compare double values, but you can you e.g.

    double z1 = line.get_Vertex(index).Z;
    double z2 = compared.get_Vertex(index).Z;
    
    const double tolerance = 0.0000001;
    
    if (Math.Abs(z1 - z2) < tolerance)

    With regards,

      Jan