How to modify Vertices in LineString (3D) with MDL

Hi everyone,

I am trying to write an MDL for Microstation V8i (Select Series 4) which should transform the heights according to a certain height-modelling for different element-types.

I am using a Loop over all graphical Elements with a callback-function, which first creates an Duplicate of the MSElementDscr-Object

mdlElmdscr_duplicate(&newedP, edP);

first and afterwards calls the specific Transformation-function according to the element-type.

It works already for some Elements, like e.g. for Line_3d-Elements:

Private int HoehTrans_htransLine
(
    MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
)
  {
    int test1=FALSE, test2=FALSE;
    
    // Height-Transformation
    test1=test_transHPoint(&(*edPP)->el.line_3d.start);
    test2=test_transHPoint(&(*edPP)->el.line_3d.end);

    if(test1==TRUE && test2==TRUE) return TRUE;
    else return FALSE;
  }

The same thing should be done for Line_String_3d-Elements as well with the following Code:

Private int HoehTrans_htransLineString
(
    MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
)
  {
    int test=FALSE i;
    int pnum;
    DPoint3d** pList;

    // Speicher fuer Punktliste allokieren
    pnum=(*edPP)->el.line_string_3d.numverts;
    pList= (DPoint3d**) calloc(pnum, sizeof(DPoint3d*));
    for(i=0; i<pnum; i++) pList[i]= (DPoint3d*) calloc(1, sizeof(DPoint3d));

    // Hoehen-Transformation
    test=HoehTrans_transHList(&(*edPP)->el.line_string_3d.vertice, pnum);
    
    if(test==TRUE) return TRUE;
    else return FALSE;
  }

The connected height-transformation test-function Looks like this:

Private int test_transHList
(
    DPoint3d** pointPL,			//<=> IN/OUT: zu transformierende DPunkt3d-Liste
    int pnum					// => IN: Anzahl der Punkte in Liste
)
  {
    double dh=150.0;
    int ret=FALSE, i;
    
    if(pointPL!=NULL)
    {
        for(i=0; i<pnum; i++) pointPL[i]->z+=dh;
        ret=TRUE;
    }
    return ret;
  }

My Problem is now, that I get an Compiler-error  in the function "HoehTrans_htransLineString" for the Line

-    test=HoehTrans_transHList(&(*edPP)->el.line_string_3d.vertice, pnum);

that says:

"conversion of Parameter 1 from  'DPoint3d (*)[1]' in 'DPoint3d **' not possible"

Because the number of Vertices in the Element is not only "ONE", why  is the '(&(*edPP)->el.line_string_3d.vertice'-Parameter of the LineString-Element not  a field-pointer 'DPoint3d**' ??

Did I understand something wrong, or is it just a simple error in the Code?

In case of the Line-Element I rewrite finally in the callback-function the changed MSElementDscr-Object to file using and would like to do that with the LineString in the same way:

mdlElmdscr_rewrite(newedP, NULL, filePos);

Many thanks in advance for your help!

Best regards,

Ines Wieland

Parents
  • The connected height-transformation test-function

    You're digging too deep into the element structure.  Use the tools available to you that use the power of the API.  The key word in your above statement is 'transformation'.  You can use a Transform to move any type of element.

    In other words, don't do this...

    double dh=150.0;
    int ret=FALSE, i;
        
    if(pointPL!=NULL)
    {
      for(i=0; i<pnum; i++) pointPL[i]->z+=dh;
      ret=TRUE;
    }

    Prefer to make a transform and apply it to your element...

    #include <mselmdsc.fdf>
    #include <mstmatrx.fdf>
    Transform transform;
    mdlTMatrix_getIdentity (&transform);
    const double elevate = 150.0;
    DPoint3d offset = { 0., 0., elevate };
    mdlTMatrix_setTranslation (&transform, &offset);

    ...

    mdlElmdscr_transform (newedP, &transform);

    Note that this approach is higher-level and you don't need to know about the element vertices or how they are stored.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    thanks for your answer, but you got me wrong: I only used this simple test-function (with only adding a constant height), because the real function is much more complicate and depending on the position of each point, so there is no possibility to move the whole element, but to adjust the height for each coordinate.

    The way you described is already used for Point-Elements (like Cells and Texts), but the height-correction varies in the area, like an area-function dh=f(x,y), which is already implemented, therefore for Lines, Linestrings, Shapes ... I need to manipulate the height of each vertex separateley

    Best regards,

    Ines Wieland

  • I need to manipulate the height of each vertex separateley

    My earlier comment still stands: use a higher-level C++ approach and eschew the C-style memory allocation.

    Read this article about the C++ standard library and using std::vector<DPoint3d>.

    #include <vector>   //	Standard Library header for vector template class
    typedef std::vector<DPoint3d>   DPoint3dCollection;  //	typedef clarifies subsequent code
    
    void ExtractPoints (MSElement const* linearElement, DgnModelRefP modelRef)
    {
      int pointCount   = mdlLinear_getPointCount (linearElement);
      DPoint3dCollection  points (pointCount);
      //	Ensure that the vector knows how many points it contains
      points.resize (pointCount);
      if (SUCCESS == mdlLinear_extract (&points[0], &pointCount, linearElement, modelRef))
      {
        // Do something with points ...
        for (int i = 0; i != pointCount; ++i)
        {
          DPoint3d& point = points [i];
          point.z = 1234.;
        }
      }
      //  No need to free() anything
    }

     
    Regards, Jon Summers
    LA Solutions

    Answer Verified By: Ines Wieland 

  • Private int HoehTrans_htransLine
    (
    MSElementDescr **edPP // <=> Zeiger auf MSElementDescr-Objekt
    )
    {
    int test1=FALSE, test2=FALSE;

    // Height-Transformation
    test1=test_transHPoint(&(*edPP)->el.line_3d.start);
    test2=test_transHPoint(&(*edPP)->el.line_3d.end);

    if(test1==TRUE && test2==TRUE) return TRUE;
    else return FALSE;
    }

    ^ The serious problem with this approach is that it doesn't update line element's range, so now you have a line element with end points that are potentially outside it's range and that will be the source of all kinds of problems.

    As Jon points out, the standard approach to modification is to extract the information, update it, then feed it back to a create function where you supply the original element as the template.

    HTH

    -B



  • Many thanks Jon, that was very helpful!

    Now my functions look like this:

    Private int HoehTrans_htransLine
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElement newLine;		//Neues Line-Element mit geaenderten Koordinaten
        int pnum = mdlLinear_getPointCount(&(*edPP)->el);
        DPoint3dVec pList(pnum);
        pList.resize(pnum);	
    
        if (SUCCESS == mdlLinear_extract (&pList[0], &pnum, &(*edPP)->el, NULL))
       {
            // Height-Transformation
            if(test_transHList(&pList[0], pnum) != TRUE) return (-HTV1_ERR_TRANSFORM_IMPOSSIBLE);
    
            // Create New Line-Element and replace in MSElement-Descriptor
    	   if(mdlLine_create(&newLine, &(*edPP)->el, &pList[0]) == SUCCESS)
    	   {
    	       if(mdlElmdscr_replaceElement(edPP, &newLine)== SUCCESS) return SUCCESS;
    		  else return -MDLERR_INSFMEMORY;
            }
        }
        return -1;
    }
    

    This should solve the problem Brien mentioned below as well I think.

    The function for the linestring- and shape-elements looks like this:

    Private int HoehTrans_htransLineString
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElement newEl;		//New Element with changed coordinates
        int pnum = mdlLinear_getPointCount(&(*edPP)->el);
        int elType = mdlElement_getType(&(*edPP)->el);
        int t_create=-1;
        int fillmode=-1;
        
        // Create Point-Vektor
        DPoint3dVec pList(pnum);
        pList.resize(pnum);
    
        if (SUCCESS == mdlLinear_extract (&pList[0], &pnum, &(*edPP)->el, NULL))
       {
            // Height-Transformation
            if(test_transHList(&pList[0], pnum) != TRUE) return -1;
    
            // Create new element according to Element-Type
            switch(elType)
            {
              case LINE_STRING_ELM:		//case 4
                  t_create=mdlLineString_create(&newEl, &(*edPP)->el, &pList[0], pnum);
                  break;
              case SHAPE_ELM:			//case 6
                  if(pnum<3) return -1;	// evtl. anderer Fehlercode??
                  fillmode=mdlElement_isFilled(&(*edPP)->el);
                  t_create=mdlShape_create(&newEl, &(*edPP)->el, &pList[0], pnum, fillmode);
                  break;
              default:					// other cases
              	return -1;
                  break;
            }
            if(t_create != SUCCESS) return -1;
    
            // Replace element in ElementDescr-Object
            if(mdlElmdscr_replaceElement(edPP, &newEl)== SUCCESS) return SUCCESS;
            else return -1;
        }
        return -1;
    }
    

    The function for arc- and ellipse-elements looks like this:

    Private int HoehTrans_htransEllipse
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        int test=-1;
        double dh=0.0;
        DPoint3d pOrigin;
        DPoint3d ptrans;
        Transform tMatrix;
        
        // Extracting origin-coordinate
        if(mdlArc_extract(NULL, NULL, NULL, NULL, NULL, NULL, &pOrigin, &(*edPP)->el) != SUCCESS) return -MDLERR_BADELEMENT;
    
        // Calculate Height-Shift with separate function
        dh=test_calcHShift(&pOrigin, &test);
    
        if(test = TRUE)
        {
          // Transformation (Translation in Z)
          ptrans.x=0.0;
          ptrans.y=0.0;
          ptrans.z = dh;
          mdlTMatrix_getIdentity(&tMatrix);
          mdlTMatrix_setTranslation(&tMatrix, &ptrans);
          if(mdlElmdscr_transform((*edPP), &tMatrix) == SUCCESS) return SUCCESS;
          else return -1;
        }
      }
    

    To deal as well with complex linestrings and shapes i wrote the following function, which only uses the above mentioned functions for each sub-element (if necessary recursively):

    Private int HoehTrans_htransComplex
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElementDescr *pComponent = (*edPP)->h.firstElem;
        int elType = mdlElement_getType(&(*edPP)->el);
        int test = -1;
        
        if(elType != CMPLX_STRING_ELM  && elType != CMPLX_SHAPE_ELM) return -1;
        
        // Loop over all components
        while(pComponent)
        {
    	   elType = mdlElement_getType(&pComponent->el);
    	   
    	   // call transformation-function according to element-type
    	   switch(elType)
    	   {
    		case LINE_ELM:				//case 3
    			test=HoehTrans_htransLine(&pComponent);
    			break;
    		case LINE_STRING_ELM:		//case 4
    		case SHAPE_ELM:			//case 6
    			test=HoehTrans_htransLineString(&pComponent);
    			break;
    		case CMPLX_STRING_ELM:		//case 12
    		case CMPLX_SHAPE_ELM:		//case 14
    			test=HoehTrans_htransComplex(&pComponent);
    			break;
    		case ARC_ELM:				//case 15
    			test=HoehTrans_htransEllipse(&pComponent);
    			break;
    		default:					// other cases
    			return -1;
    			break;
    		}	// Ende SWITCH-Block
    		if(test != SUCCESS) return -1;
    		
    		// go to next Element
    		pComponent = pComponent->h.next;
    	}
        // Deallocate memory allocated by Microstation
        mdlElmdscr_freeAll(&pComponent);
    
        return SUCCESS;
      }
    

    That seems to work, but there are some cases, where the heights are addionally corrected for each sub-element.
    Here are two screenshots to make it clear. Before the transformation (side-view):

    And after the transformation (side-view):

    (isometric view):

    (The yellow cone-element below is not transformed and is used as 'reference'-check)

    The green elements are splittet in height, as if the shift-values were added from each prior element, and not from original height. I looked for the 'mdlCurrTrans_getCount'-value, if there was added any translation from the 'HoehTrans_htransEllipse'-function to the current transformation but this does not seemed to be the case. The problem seems only to appear AFTER at least one Arc-Element was processed inside the complex element.

    Has anyone an idea or hint, what might be the reason for this behaviour?

    Many thanks and best regards
    Ines Wieland

Reply
  • Many thanks Jon, that was very helpful!

    Now my functions look like this:

    Private int HoehTrans_htransLine
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElement newLine;		//Neues Line-Element mit geaenderten Koordinaten
        int pnum = mdlLinear_getPointCount(&(*edPP)->el);
        DPoint3dVec pList(pnum);
        pList.resize(pnum);	
    
        if (SUCCESS == mdlLinear_extract (&pList[0], &pnum, &(*edPP)->el, NULL))
       {
            // Height-Transformation
            if(test_transHList(&pList[0], pnum) != TRUE) return (-HTV1_ERR_TRANSFORM_IMPOSSIBLE);
    
            // Create New Line-Element and replace in MSElement-Descriptor
    	   if(mdlLine_create(&newLine, &(*edPP)->el, &pList[0]) == SUCCESS)
    	   {
    	       if(mdlElmdscr_replaceElement(edPP, &newLine)== SUCCESS) return SUCCESS;
    		  else return -MDLERR_INSFMEMORY;
            }
        }
        return -1;
    }
    

    This should solve the problem Brien mentioned below as well I think.

    The function for the linestring- and shape-elements looks like this:

    Private int HoehTrans_htransLineString
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElement newEl;		//New Element with changed coordinates
        int pnum = mdlLinear_getPointCount(&(*edPP)->el);
        int elType = mdlElement_getType(&(*edPP)->el);
        int t_create=-1;
        int fillmode=-1;
        
        // Create Point-Vektor
        DPoint3dVec pList(pnum);
        pList.resize(pnum);
    
        if (SUCCESS == mdlLinear_extract (&pList[0], &pnum, &(*edPP)->el, NULL))
       {
            // Height-Transformation
            if(test_transHList(&pList[0], pnum) != TRUE) return -1;
    
            // Create new element according to Element-Type
            switch(elType)
            {
              case LINE_STRING_ELM:		//case 4
                  t_create=mdlLineString_create(&newEl, &(*edPP)->el, &pList[0], pnum);
                  break;
              case SHAPE_ELM:			//case 6
                  if(pnum<3) return -1;	// evtl. anderer Fehlercode??
                  fillmode=mdlElement_isFilled(&(*edPP)->el);
                  t_create=mdlShape_create(&newEl, &(*edPP)->el, &pList[0], pnum, fillmode);
                  break;
              default:					// other cases
              	return -1;
                  break;
            }
            if(t_create != SUCCESS) return -1;
    
            // Replace element in ElementDescr-Object
            if(mdlElmdscr_replaceElement(edPP, &newEl)== SUCCESS) return SUCCESS;
            else return -1;
        }
        return -1;
    }
    

    The function for arc- and ellipse-elements looks like this:

    Private int HoehTrans_htransEllipse
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        int test=-1;
        double dh=0.0;
        DPoint3d pOrigin;
        DPoint3d ptrans;
        Transform tMatrix;
        
        // Extracting origin-coordinate
        if(mdlArc_extract(NULL, NULL, NULL, NULL, NULL, NULL, &pOrigin, &(*edPP)->el) != SUCCESS) return -MDLERR_BADELEMENT;
    
        // Calculate Height-Shift with separate function
        dh=test_calcHShift(&pOrigin, &test);
    
        if(test = TRUE)
        {
          // Transformation (Translation in Z)
          ptrans.x=0.0;
          ptrans.y=0.0;
          ptrans.z = dh;
          mdlTMatrix_getIdentity(&tMatrix);
          mdlTMatrix_setTranslation(&tMatrix, &ptrans);
          if(mdlElmdscr_transform((*edPP), &tMatrix) == SUCCESS) return SUCCESS;
          else return -1;
        }
      }
    

    To deal as well with complex linestrings and shapes i wrote the following function, which only uses the above mentioned functions for each sub-element (if necessary recursively):

    Private int HoehTrans_htransComplex
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElementDescr *pComponent = (*edPP)->h.firstElem;
        int elType = mdlElement_getType(&(*edPP)->el);
        int test = -1;
        
        if(elType != CMPLX_STRING_ELM  && elType != CMPLX_SHAPE_ELM) return -1;
        
        // Loop over all components
        while(pComponent)
        {
    	   elType = mdlElement_getType(&pComponent->el);
    	   
    	   // call transformation-function according to element-type
    	   switch(elType)
    	   {
    		case LINE_ELM:				//case 3
    			test=HoehTrans_htransLine(&pComponent);
    			break;
    		case LINE_STRING_ELM:		//case 4
    		case SHAPE_ELM:			//case 6
    			test=HoehTrans_htransLineString(&pComponent);
    			break;
    		case CMPLX_STRING_ELM:		//case 12
    		case CMPLX_SHAPE_ELM:		//case 14
    			test=HoehTrans_htransComplex(&pComponent);
    			break;
    		case ARC_ELM:				//case 15
    			test=HoehTrans_htransEllipse(&pComponent);
    			break;
    		default:					// other cases
    			return -1;
    			break;
    		}	// Ende SWITCH-Block
    		if(test != SUCCESS) return -1;
    		
    		// go to next Element
    		pComponent = pComponent->h.next;
    	}
        // Deallocate memory allocated by Microstation
        mdlElmdscr_freeAll(&pComponent);
    
        return SUCCESS;
      }
    

    That seems to work, but there are some cases, where the heights are addionally corrected for each sub-element.
    Here are two screenshots to make it clear. Before the transformation (side-view):

    And after the transformation (side-view):

    (isometric view):

    (The yellow cone-element below is not transformed and is used as 'reference'-check)

    The green elements are splittet in height, as if the shift-values were added from each prior element, and not from original height. I looked for the 'mdlCurrTrans_getCount'-value, if there was added any translation from the 'HoehTrans_htransEllipse'-function to the current transformation but this does not seemed to be the case. The problem seems only to appear AFTER at least one Arc-Element was processed inside the complex element.

    Has anyone an idea or hint, what might be the reason for this behaviour?

    Many thanks and best regards
    Ines Wieland

Children
  • what might be the reason for this behaviour?

    I can see one potential reason for erratic behaviour:

    Private int HoehTrans_htransComplex
    (
        MSElementDescr **edPP 
    )
    {
        MSElementDescr *pComponent = (*edPP)->h.firstElem;
        while(pComponent)
        {
            ...
            pComponent = pComponent->h.next;
    } // Deallocate memory allocated by Microstation mdlElmdscr_freeAll (&pComponent); return SUCCESS; }

    Pointer pComponent lets you look into the complex descriptor — but you don't own the memory it points to.  You're freeing memory pointed to by the last value of pComponent, but that pointer must be NULL after the loop. I don't know the behaviour of mdlElmdscr_freeAll() when handed a NULL, but the call is unnecessary.

     
    Regards, Jon Summers
    LA Solutions

  • I tried it without the call of mdlElmdscr_freeAll() but it didn't changed anything.

    Is there any side-effect when using the function mdlElmdscr_transform()for the current transformation? Or is there any effect on the whole complex element by this function? I noticed by logging the origin-heights inside the complex string, that the height from the next element after an arc-element (see HoehTrans_htransEllipse-function above), which is extracted with the mdlArc_extract-function, has already the last height-shift dh, before the transformation was done!??

    I thought, that the defined transformation-matrix is only valid inside the HoehTrans_htransEllipse-function and have therefore no idea what causes this problem.

    Best regards,
    Ines Wieland

  • is there any effect on the whole complex element by this function?

    You've modified the 3D range of components of the complex header.  Have you called mdlElmdscr_validate() to force recomputation of the complex header's range block?

     
    Regards, Jon Summers
    LA Solutions

  • Have you called mdlElmdscr_validate() to force recomputation of the complex header's range block?

    Thanks for that advice - I haven't yet thought on this but would assume that this is only necessary at the end of the mdHoehTrans_htransComplex-function, or must it be done after handling each sub-element (which does not make sense in my opinion)?

    In any case: I just tried it, but it does not solve the strange behaviour, because my test-examples of complex elements (for which i sent the screenshots above) do not include nested complex elements now.  

    Best regards,

    Ines Wieland

  • ...

    Correct.

    the height from the next element after an arc-element (see HoehTrans_htransEllipse-function above), which is extracted with the mdlArc_extract-function, has already the last height-shift dh, before the transformation was done!

     
    Regards, Jon Summers
    LA Solutions

  • Is there any side-effect when using the function mdlElmdscr_transform()for the current transformation? Or is there any effect on the whole complex element by this function?

    mdlElmdscr_transform follows h.next pointers. When modifying individual entries in an element descriptor chain, or components of a complex element you would typically want to use mdlElmdscr_duplicateSingle before calling mdlElmdscr_transform to isolate the component. You then use mdlElmdscr_replace to update the original.



    Answer Verified By: Ines Wieland 

  • Many thanks Brien, that solves the displacement-problem!

    I changed the mdHoehTrans_htransComplex-function in the following way and it works now:

    Private int HoehTrans_htransComplex
    (
        MSElementDescr	**edPP		// <=> Zeiger auf MSElementDescr-Objekt
    )
      {
        MSElementDescr *pComponent = (*edPP)->h.firstElem;
        MSElementDescr *pPartial;
        int elType = mdlElement_getType(&(*edPP)->el);
        int test = -1;
        
        if(elType != CMPLX_STRING_ELM  && elType != CMPLX_SHAPE_ELM) return -1;
        
        // Loop over all components
        while(pComponent)
        {
    	   elType = mdlElement_getType(&pComponent->el);
           // Duplicate the Component for element-changes
    	   if(mdlElmdscr_duplicateSingle(&pPartial, pComponent) != SUCCESS) return retErr;
    	   
    	   // call transformation-function according to element-type
    	   switch(elType)
    	   {
    		case LINE_ELM:				//case 3
    			test=HoehTrans_htransLine(&pPartial);
    			break;
    		case LINE_STRING_ELM:		//case 4
    		case SHAPE_ELM:			//case 6
    			test=HoehTrans_htransLineString(&pPartial);
    			break;
    		case CMPLX_STRING_ELM:		//case 12
    		case CMPLX_SHAPE_ELM:		//case 14
    			test=HoehTrans_htransComplex(&pPartial);
    			break;
    		case ARC_ELM:				//case 15
    			test=HoehTrans_htransEllipse(&pPartial);
    			break;
    		default:					// other cases
    			return -1;
    			break;
    		}	// Ende SWITCH-Block
    		if(test != SUCCESS) return -1;
            
            // replace changed Element-Descriptor
    		mdlElmdscr_replaceDscr(&pComponent, pPartial);
    		
    		// go to next Element
    		pComponent = pComponent->h.next;
    	}
        // Validation to update boundary-box etc.
        mdlElmdscr_validate((*edPP), mdlModelRef_getActive());
    
        return SUCCESS;
      }
    

    Many thanks and best regards to all commentators

    Ines Wieland