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
Ines Wieland said: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
Ines Wieland said: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>.
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 }
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 regardsInes Wieland
Ines Wieland said: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.
pComponent
NULL
mdlElmdscr_freeAll()
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!??
mdlElmdscr_transform()
HoehTrans_htransEllipse
mdlArc_extract
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
Ines Wieland said: 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?
mdlElmdscr_validate()
Jon Summers said: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)?
mdHoehTrans_htransComplex-
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.
Correct.
Ines Wieland said: 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!
Ines Wieland said: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.