Hello guys!
Lately I have been playing around with the Solids interface in the new CONNECT API and trying to play with CurveOffset to make a smaller version of complex shapes I have. However, I found a problem:
As you can see in the picture, shape drawn with the bold line is the original and the thin shape is the element created with my code :
CurveOffsetOptions options(dist); options.SetTolerance(tolerance); CurveVectorPtr offset = bodyPath->CloneOffsetCurvesXY(options);
From the help of this function, we can see below description.
In your case, it is self-intersecting. I can replicate your case as well. If I use a left offset (dist is a negative number), results are perfect for below two cases.
In my memory, if we want to get a perfect offset for any cases, the C function mdlSolid_offsetWire is the final solution.
Thanks for your answer! However. is there a way to use the CurveVector API or SolidUtils to achieve the same functionality? Or, how do I use this function when I have ISolidKernelEntityPtr from ElementToBody function from SolidUtil namespace?
Lubo B said:is there a way to use the CurveVector API or SolidUtils to achieve the same functionality?
IMO, No. SolidUtils doesn't wrap mdlSolid_offsetWire function.
I found a very old thread to discuss same topic before, where I provided a V8XM version runnable code. We need to revise this code on CE.
communities.bentley.com/.../mdl-v8xm-i-copy-parallel-using-miter-mode
I´ve tried following code:
double tol = 0.5; BODY_TAG pWireBody; int status = mdlSolid_elementToBody(&pWireBody, nullptr, inDscr, MASTERFILE); if (mdlSolid_isSheetBody(pWireBody)) { int numFaces; TAG_ENTITY_LIST* pFaceList; status = mdlSolid_listCreate(&pFaceList); mdlSolid_getFaceList(pFaceList, pWireBody); status = mdlSolid_listCount(&numFaces, pFaceList); for (auto i = 0; i < numFaces; i++) { FACE_TAG pFace; status = mdlSolid_listNthEl(&pFace, pFaceList, i); status = mdlSolid_uncoverFace(pFace); } status = mdlSolid_listDelete(&pFaceList); } if (SUCCESS != status || !mdlSolid_isWireBody(pWireBody)) { if (pWireBody) mdlSolid_freeBody(pWireBody); return ERROR; } EDGE_TAG pEdge = NULL; auto statusOffset = mdlSolid_bodyAskFirstEdge(&pEdge, pWireBody); if (NULL == pEdge) { mdlSolid_freeBody(pWireBody); return ERROR; } TAG_ENTITY_LIST* pList = nullptr; status = mdlSolid_listCreate(&pList); mdlSolid_beginCurrTrans(ACTIVEMODEL); mdlCurrTrans_invScaleDoubleArray(&distance, &distance, 1); mdlCurrTrans_invScaleDoubleArray(&tol, &tol, 1); mdlSolid_endCurrTrans(); if (SUCCESS == mdlSolid_wireIsG1(pWireBody)) { gapFill = 0; } statusOffset = mdlSolid_offsetWire(pList, pWireBody, pEdge, normal, 1, tol, gapFill, FALSE);
I managed to get through the problem I mentioned above, but even after i rewrote the algorithm Yongan.Fu mentioned in his link, it cannot add element or element descriptor into model. Am i missing something ? There is a code:
int CreateElementByBody(MSElementDescr** resultEdp, BODY_TAG* pBody) { *resultEdp = NULL; BODY_TAG pCopy; mdlSolid_copyBody(&pCopy, *pBody); bool simplified = false; if (SUCCESS == mdlSolid_simplifyBody(&pCopy, FALSE)) simplified = true; if (SUCCESS != mdlSolid_bodyToElement(resultEdp, simplified ? pCopy : *pBody, TRUE, -1, -1, 0, NULL, ACTIVEMODEL) || NULL == *resultEdp) { if (NULL != pCopy) mdlSolid_deleteEntity(pCopy); return ERROR; } if (NULL != pCopy) mdlSolid_deleteEntity(pCopy); return (NULL == *resultEdp) ? ERROR : SUCCESS; } MdlPublic int mdlElmdscr_copyParallelExt ( MSElementDescr** outDscr, MSElementDescr* inDscr, double distance, DPoint3d* normal, int gapFill // 0 for arc fill, 1 for tangent extension fill, 2 for curve extension fill ) { BODY_TAG pWireBody; int status = mdlSolid_elementToBody(&pWireBody, nullptr,inDscr, MASTERFILE); if (mdlSolid_isSheetBody(pWireBody)) { int numFaces; TAG_ENTITY_LIST* pFaceList = nullptr; status = mdlSolid_listCreate(&pFaceList); mdlSolid_getFaceList(pFaceList, pWireBody); status = mdlSolid_listCount(&numFaces, pFaceList); for (auto i = 0; i < numFaces; i++) { FACE_TAG pFace; status = mdlSolid_listNthEl(&pFace, pFaceList, i); status = mdlSolid_uncoverFace(pFace); } status = mdlSolid_listDelete(&pFaceList); } if (SUCCESS != status || !mdlSolid_isWireBody(pWireBody)) { if (pWireBody) mdlSolid_freeBody(pWireBody); return ERROR; } EDGE_TAG pEdge; mdlSolid_bodyAskFirstEdge(&pEdge, pWireBody); if (NULL == pEdge) { mdlSolid_freeBody(pWireBody); return ERROR; } TAG_ENTITY_LIST* pList = NULL; mdlSolid_listCreate(&pList); double tol = 0.1; mdlSolid_beginCurrTrans(ACTIVEMODEL); mdlCurrTrans_invScaleDoubleArray(&distance, &distance, 1); mdlCurrTrans_invScaleDoubleArray(&tol, &tol, 1); mdlSolid_endCurrTrans(); if (SUCCESS != mdlSolid_offsetWire(pList, pWireBody, pEdge, normal, distance, tol, gapFill, FALSE)) { mdlSolid_freeBody(pWireBody); mdlSolid_listDelete(&pList); return ERROR; } int count = 0; mdlSolid_listCount(&count, pList); for (int i = 0; i < count; i++) { BODY_TAG pBody; MSElementDescr* tempEdp = NULL; mdlSolid_listNthEl(&pBody, pList, i); if (SUCCESS != CreateElementByBody(&tempEdp, &pBody) || NULL == tempEdp) { if (NULL != tempEdp) { mdlElmdscr_freeAll(&tempEdp); tempEdp = NULL; } continue; } mdlElmdscr_initOrAddToChain(outDscr, tempEdp); } mdlSolid_listDelete(&pList); return (NULL == *outDscr) ? ERROR : SUCCESS; }
Thanks for any kind of input, im kinda without ideas.
I wrote below test code, it works well in 3D model.
void OffsetWire() { if (!SelectionSetManager::GetManager().IsActive()) { mdlDialog_dmsgsPrint(L"No selection set found"); return; } ElementRefP elemRef = nullptr; DgnModelRefP modelRef = nullptr; SelectionSetManager::GetManager().GetElement(0, &elemRef, &modelRef); ElementHandle eh(elemRef, modelRef); double offsetDist = 1000, tolerance = 0.5; mdlSolid_beginCurrTrans(ACTIVEMODEL); BODY_TAG body; Transform trans; mdlSolid_elementToBody(&body, &trans, const_cast<MSElementDescrP>(eh.GetElementDescrCP()), ACTIVEMODEL); if (mdlSolid_isSheetBody(body)) { int numFaces = 0; TAG_ENTITY_LIST* pFaceList = NULL; mdlSolid_listCreate(&pFaceList); mdlSolid_getFaceList(pFaceList, body); mdlSolid_listCount(&numFaces, pFaceList); for (int i = 0; i < numFaces; i++) { FACE_TAG pFace = NULL; mdlSolid_listNthEl(&pFace, pFaceList, i); mdlSolid_uncoverFace(pFace); } mdlSolid_listDelete(&pFaceList); } TAG_ENTITY_LIST* pOffsetList; mdlSolid_listCreate(&pOffsetList); EDGE_TAG pEdge; mdlSolid_bodyAskFirstEdge(&pEdge, body); DPoint3d normal = DPoint3d::From(0, 0, 1); mdlCurrTrans_invScaleDoubleArray(&offsetDist, &offsetDist, 1); mdlCurrTrans_invScaleDoubleArray(&tolerance, &tolerance, 1); int gapFill = (SUCCESS == mdlSolid_wireIsG1(body)) ? 0 : 1; if (SUCCESS != mdlSolid_offsetWire(pOffsetList, body, pEdge, &normal, offsetDist, tolerance, gapFill, false)) mdlDialog_dmsgsPrint(L"mdlSolid_offsetWire failed"); else for (TAG_ENTITY_LIST::iterator iter = pOffsetList->begin(); iter != pOffsetList->end(); ++iter) { if (mdlSolid_isWireBody((*iter))) { MSElementDescrP elmdscr = NULL; mdlSolid_bodyToElement(&elmdscr, (*iter), true, 0, 0, 0, NULL, ACTIVEMODEL); mdlElmdscr_transform(&elmdscr, &trans); EditElementHandle eeh(elmdscr, true, false); eeh.AddToModel(); } } mdlSolid_listDelete(&pOffsetList); mdlSolid_freeBody(body); mdlSolid_endCurrTrans(); }
Answer Verified By: Lubo B
Thanks! That helped me a lot and also solved my problem.