|
How can I compute the intersection point, if any, between an AcDbFace entity and an AcDbLine?
Solution
There are mainly two ways you can do that using arx, the following DevNote exposes both:
1 - First technique create an infinite plane and infinite line, then uses a ray to check if the intersection point of these two infinite entities is inside the face.
2 - Second technique uses 3D solids. It creates a thin extruded solid from the face and another to represent the line, then uses checkInterferences method to find out the intersection point.
Below are the samples for these functions, along with a test function:
/////////////////////////////////////////////////////////////////////
//Use: Intersection between Face and Line using Ray
//
/////////////////////////////////////////////////////////////////////
void FindIntersection(AcDbFace* pFace, AcDbLine* pLine)
{
AcGePoint3d vertex1, vertex2, vertex3, vertex4;
pFace->getVertexAt(0, vertex1);
pFace->getVertexAt(1, vertex2);
pFace->getVertexAt(2, vertex3);
pFace->getVertexAt(3, vertex4);
AcGeVector3d vecU(vertex2.asVector()-vertex1.asVector());
AcGeVector3d vecV(vertex3.asVector()-vertex1.asVector());
//Create temp plane
AcGePlane plane(vertex1, vecU, vecV);
//Create temp line
AcGeLineSeg3d line (pLine->startPoint(), pLine->endPoint());
//Check intersection
AcGePoint3d resultPoint;
if (plane.intersectWith(line, resultPoint) == Adesk::kTrue)
{
AcGeRay3d ray(resultPoint, vertex1.asVector() - vertex2.asVector());
int intNum = 0;
//check if intersection point is inside the face
AcGePoint3d intPoint;
if (ray.intersectWith(AcGeLineSeg3d(vertex1, vertex2), intPoint)
== Adesk::kTrue) ++intNum;
if (ray.intersectWith(AcGeLineSeg3d(vertex2, vertex3), intPoint)
== Adesk::kTrue) ++intNum;
if (ray.intersectWith(AcGeLineSeg3d(vertex3, vertex4), intPoint)
== Adesk::kTrue) ++intNum;
if (ray.intersectWith(AcGeLineSeg3d(vertex4, vertex1), intPoint)
== Adesk::kTrue) ++intNum;
//if intNum is 'odd' then point is inside
if((intNum%2) != 0)
{
acutPrintf (L"\nIntersection point: [%f, %f, %f]",
resultPoint.x, resultPoint.y, resultPoint.z);
}
}
}
/////////////////////////////////////////////////////////////////////
//Use: Intersection between Face and Line using 3D Solids
//
/////////////////////////////////////////////////////////////////////
void FindIntersection2(AcDbFace* pFace, AcDbLine* pLine)
{
//Line direction
AcGeVector3d direction (pLine->endPoint()-pLine->startPoint());
//Create temp circle
AcDbCircle* pcircle =
new AcDbCircle(pLine->startPoint(), direction, 0.01);
//Create temp solid line
AcDb3dSolid* pSolidLine = new AcDb3dSolid ();
AcDbSweepOptions sweepOptions;
pSolidLine->createExtrudedSolid(pcircle, direction, sweepOptions);
//Create temp solid Face
AcDb3dSolid *pSolidFace = new AcDb3dSolid ();
AcGePoint3d vertex1, vertex2, vertex3;
pFace->getVertexAt(0, vertex1);
pFace->getVertexAt(1, vertex2);
pFace->getVertexAt(2, vertex3);
AcGeVector3d vec1(vertex2.asVector()-vertex1.asVector());
AcGeVector3d vec2(vertex3.asVector()-vertex1.asVector());
//Compute face's normal
AcGeVector3d faceNormal = vec2.crossProduct(vec1);
faceNormal *= 0.01 / faceNormal.length();
//Create a very thin extruded solid
pSolidFace->createExtrudedSolid(pFace, faceNormal, sweepOptions);
//Check interference between the two solids
Adesk::Boolean IsIntersec;
AcDb3dSolid* commonVolumeSolid;
Acad::ErrorStatus es =
pSolidFace->checkInterference(
pSolidLine,
Adesk::kTrue,
IsIntersec,
commonVolumeSolid);
if (IsIntersec == Adesk::kTrue)
{
double volume;
AcGePoint3d centroid;
double momInertia[3], prodInertia[3], prinMoments[3],
radiiGyration[3];
AcGeVector3d prinAxes[3];
AcDbExtents extents;
commonVolumeSolid->getMassProp(
volume,
centroid,
momInertia,
prodInertia,
prinMoments,
prinAxes,
radiiGyration,
extents);
acutPrintf (L"\nIntersection point: [%f, %f, %f]",
centroid.x, centroid.y, centroid.z);
delete commonVolumeSolid;
}
delete pcircle;
delete pSolidLine;
delete pSolidFace;
}
//The test method
void FindIntersectionTest(void)
{
ads_name ename;
ads_point pickpt;
AcDbObjectId objId;
AcDbObject *pObj;
int rc;
rc= acedEntSel(L"\nSelect Face: ", ename, pickpt);
if(rc != RTNORM)
{
if (rc != RTCAN) acutPrintf(L"\nError selecting entity ");
return;
}
acdbGetObjectId(objId, ename);
acdbOpenObject(pObj, objId, AcDb::kForRead);
AcDbFace* pEntity1 = AcDbFace::cast(pObj);
if(!pEntity1)
{
acutPrintf(L"\nSelection Invalid...");
pObj->close();
return;
}
rc= acedEntSel(L"\nSelect line: ", ename, pickpt);
if(rc != RTNORM)
{
if (rc != RTCAN) acutPrintf(L"\nError selecting entity ");
return;
}
acdbGetObjectId(objId, ename);
acdbOpenObject(pObj, objId, AcDb::kForRead);
AcDbLine* pEntity2 = AcDbLine::cast(pObj);
if(!pEntity2)
{
acutPrintf(L"\nSelection Invalid...");
pObj->close();
return;
}
FindIntersection (pEntity1, pEntity2);
FindIntersection2(pEntity1, pEntity2);
pEntity1->close();
pEntity2->close();
}
How to Intersect a line with a solid?
[url]https://adndevblog.typepad.com/autocad/2013/02/how-to-intersect-a-line-with-a-solid.html[/url]
This is possible using the B-Rep API. The code snippet below illustrates how you can find the intersections of a linear entity ( LINE, XLINE or RAY ) with an entity represented using ACIS (that is a SOLID, REGION, or BODY).
// This is command 'INTSOLID'
void Asdkintsolid()
{
AcDbObjectId lineId;
AcDbObjectId solidId;
AcDbEntity * pEnt;
AcDbEntity * pLinearEnt;
AcGePoint3dArray hitPoints;
int nHitsWanted;
ads_name en ;
ads_point pt ;
// User picks a line
if ( acedEntSel (_T("\nSelect a line: "), en, pt) != RTNORM )
return;
// Get pointer to line
if ( acdbGetObjectId (lineId, en) != Acad::eOk )
{
return;
}
acdbOpenAcDbEntity(pLinearEnt, lineId, AcDb::kForRead );
if(pLinearEnt == NULL )
{
acutPrintf(_T("\nCannot open line"));
return;
}
// User picks a solid
if ( acedEntSel (_T("\nSelect a solid: "), en, pt) != RTNORM )
return;
// Get pointer to line
if ( acdbGetObjectId (solidId, en) != Acad::eOk )
{
return;
}
acdbOpenAcDbEntity(pEnt, solidId, AcDb::kForRead );
if(pEnt == NULL )
{
acutPrintf(_T("\nCannot open solid"));
pLinearEnt->close();
return;
}
// Get required number of intersections to be returned.
// If user enters zero, then we will return all intersections.
if (RTNORM != acedGetInt(_T("\nEnter number of Hits required : "),
&nHitsWanted))
{
acutPrintf(_T("\nYou must enter an integer"));
pEnt->close();
pLinearEnt->close();
return;
}
// Find number of intersections
if ( Acad::eOk != getHits( pLinearEnt, pEnt,
nHitsWanted, hitPoints))
{
pEnt->close();
pLinearEnt->close();
return;
}
// Entities not needed any more
pEnt->close();
pLinearEnt->close();
// Print out intersections
int len = hitPoints.length();
if ( len == 0 )
{
acutPrintf(_T("\nNo intersections found."));
}
else
{
acutPrintf(_T("\n%d intersections found."), len);
for (int i = 0; i < len; i++)
{
AcGePoint3d pt = hitPoints[i];
acutPrintf(_T("\nIntersection %d: (%f, %f, %f)"),
i+1, pt[X], pt[Y], pt[Z]);
}
}
return;
}
Acad::ErrorStatus getHits( AcDbEntity * pLinearEnt,
AcDbEntity * pEnt,
int nHitsWanted,
AcGePoint3dArray &hitPoints)
// Parameters:
// AcDbEntity * pLinearEnt - pointer to a line
// AcDbEntity * pEnt - pointer to a solid
// int nHitsWanted - number of intersections
// to return (0 means return all)
// AcGePoint3dArray hitPoints - array to
// return intersection points
// Returns:
// Acad::eOk - if all is well
// Acad::eInvalidInput - if there is a problem
{
// Check that solid pointer is a really a solid
if( !(pEnt->isKindOf(AcDb3dSolid::desc()) ||
pEnt->isKindOf(AcDbRegion::desc()) ||
pEnt->isKindOf(AcDbBody::desc()) ) )
{
acutPrintf(_T("\nSecond argument must be a solid, not a %s"),
pEnt->isA()->name());
return Acad::eInvalidInput;
}
// Check that the line pointer is a line.
// If it is, create a corresponding AcGe object.
AcGeLinearEnt3d * pGeLine;
if(pLinearEnt->isKindOf( AcDbLine::desc() )) {
AcDbLine * pLine = (AcDbLine*)pLinearEnt;
pGeLine = new AcGeLineSeg3d(pLine->startPoint(),
pLine->endPoint() );
} else if (pLinearEnt->isKindOf( AcDbRay::desc() )) {
AcDbRay * pRay = (AcDbRay*)pLinearEnt;
pGeLine = new AcGeRay3d(pRay->basePoint(), pRay->unitDir() );
} else if (pLinearEnt->isKindOf( AcDbXline::desc() )) {
AcDbXline * pXline = (AcDbXline *)pLinearEnt;
pGeLine = new AcGeLine3d(pXline->basePoint(),
pXline->unitDir() );
} else {
acutPrintf(_T("\nFirst argument must be a line, not a %s"),
pLinearEnt->isA()->name());
return Acad::eInvalidInput;
}
// Find the number of intersections using B-Rep API
AcBrBrep*pBrep = new AcBrBrep;
AcDbObjectId solidId = pEnt->objectId();
pBrep->setSubentPath( AcDbFullSubentPath( solidId,
AcDbSubentId()));
AcBrHit*pHits = NULL;
Adesk::UInt32 nHitsFound;
pBrep->getLineContainment(*pGeLine, nHitsWanted,
nHitsFound, pHits );
// Append valid intersection points to hitPoints
for( Adesk::UInt32 i=0;i<nHitsFound; i++ ) {
AcBrEntity*pHitEntity = NULL;
pHits[i].getEntityHit( pHitEntity );
if( pHitEntity == NULL )
continue;
AcGePoint3d hitPt;
pHits[i].getPoint(hitPt);
if( !pHitEntity->isKindOf(AcBrBrep::desc()) ) {
hitPoints.append( hitPt );
}
else
{
acutPrintf(
_T("\nIgnoring the point at (%f, %f, %f) with a %s"),
hitPt.x, hitPt.y, hitPt.z, pHitEntity->isA()->name());
}
delete pHitEntity;
}
// Convert points from WCS to UCS
int nPts = hitPoints.length();
for( int i=0; i<nPts;i++ ) {
ads_point pt;
asPnt3d(pt) = hitPoints[i];
acdbWcs2Ucs(pt,pt,0);
hitPoints[i] = asPnt3d(pt);
}
// Tidy up
delete [] pHits;
delete pBrep;
delete pGeLine;
// end of function - everything must have worked
return Acad::eOk;
}
Selected point on solid during entity selection
[url]https://adndevblog.typepad.com/autocad/2015/06/selected-point-on-solid-during-entity-selection.html[/url]
The acedEntSel method provides a way for the user to select an entity and it also provides the point that was used during the entity selection. When used on a 2D entity, the point returned by acedEntSel is quite useful if you need that information to further work on the entity and perform tasks such as break, trim etc.
However, when acedEntSel is used to select a solid, the point returned is in the UCS XY Plane regardless of where on the solid you pick. if you use an OSNAP while selecting the solid entity, the point will be on the solid. If the point on the solid that was used while selecting the solid is important for doing further processing in your code, here is a way to get that.
In the following code, a ray is created along the viewing direction with which we will find the intersection points of the solid. The code displays all the intersection points as though the ray pierced the solid. But the first intersection point should provide the selection point, in case you are only interested in that.
Please note that, in AutoCAD 2015 and AutoCAD 2016, the "AcBrBrep::getLineContainment" returns lesser number of hits during an intersection test. This behavior has been logged with our engineering team. AutoCAD 2014 and previous releases provide the right number of hits.
Here is a recording which demonstrates the entity selection and the sample code :
Acad::ErrorStatus es;
// pt is in UCS coordinates and in the UCS XY plane
// unless a osnap is used while selecting the entity
// In that case, the point will be have all
// the three coordinate values in UCS.
ads_name ename;
ads_point pt;
int ret = acedEntSel(_T("\\nPick a solid : " ), ename, pt);
if (ret != RTNORM)
return ;
AcDbObjectId entId = AcDbObjectId::kNull;
es = acdbGetObjectId(entId, ename);
AcDbEntity *pEnt = NULL;
AcDb3dSolid *pSolid = NULL;
es = acdbOpenAcDbEntity(pEnt, entId, AcDb::kForRead);
pSolid = AcDb3dSolid::cast(pEnt);
if (pSolid == NULL)
{
acutPrintf(L"\\nSelected entity was not a solid.\\n" );
return ;
}
// Get the WCS coordinates of the selected point
AcGePoint3d wcsPt = AcGePoint3d::kOrigin;
acdbUcs2Wcs(pt, pt, 0);
wcsPt = asPnt3d(pt);
resbuf viewRb;
AcGePoint3d target = AcGePoint3d::kOrigin;
// TARGET is in UCS
acedGetVar(_T("TARGET" ), &viewRb);
target = asPnt3d(viewRb.resval.rpoint);
acdbUcs2Wcs(asDblArray(target), asDblArray(target), 0);
AcGeVector3d dir = AcGeVector3d::kIdentity;
// VIEWDIR is in UCS
acedGetVar( ACRX_T("VIEWDIR" ), &viewRb );
dir = asVec3d(viewRb.resval.rpoint);
acdbUcs2Wcs(asDblArray(dir), asDblArray(dir), 1);
AcGePoint3d position = target + dir;
dir = dir.normalize().negate();
AcGeLine3d testRay(
wcsPt.project(AcGePlane(position, dir), dir.negate()),
dir);
AcBrBrep*pBrep = new AcBrBrep();
pBrep->setSubentPath( AcDbFullSubentPath
( pEnt->objectId(), AcDbSubentId()));
AcBr::ErrorStatus bres;
AcBrHit *pHits = NULL;
Adesk::UInt32 nHitsFound = 0;
// Find all hits
bres = pBrep->getLineContainment(
testRay, 0, nHitsFound, pHits );
if (bres == AcBr::eOk)
{
for ( Adesk::UInt32 i = 0; i < nHitsFound; i++)
{
AcBrEntity *pHitEntity = NULL;
pHits[i].getEntityHit( pHitEntity );
if ( pHitEntity == NULL )
continue ;
if ( ! pHitEntity->isKindOf(AcBrBrep::desc()))
{
AcGePoint3d hitPt;
pHits[i].getPoint(hitPt);
AcDbPoint *pDbPt = new AcDbPoint(hitPt);
PostToDb(pDbPt);
es = pDbPt->close();
}
delete pHitEntity;
}
delete [] pHits;
}
delete pBrep;
pEnt->close();
[url]https://github.com/kevinzhwl/ObjectARXCore/blob/master/2015/utils/brep/samples/brepsamp/brlncnt.cpp[/url]
//
// Copyright 2014 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
// DESCRIPTION:
//
// Source file for the ObjectARX application command "BRLNCNT".
#include "brsample_pch.h" //precompiled header
// include here
void
lineContainment()
{
AcBr::ErrorStatus returnValue = AcBr::eOk;
// Select the entity by type
AcBrEntity* pEnt = NULL;
AcDb::SubentType subType = AcDb::kNullSubentType;
returnValue = selectEntityByType(pEnt, subType);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in selectEntityByType:"));
errorReport(returnValue);
delete pEnt;
return;
}
// Query the line by AutoCAD pick
AcGePoint3d startPt, endPt;
int hitCount = 0;
acutPrintf(ACRX_T("\n Pick line for containment test, by selecting two points: \n"));
acedGetPoint(NULL, ACRX_T("\n Pick origin of line: \n"), asDblArray(startPt));
acedGetPoint(NULL, ACRX_T("\n Pick another point on line: \n"), asDblArray(endPt));
acedGetInt(ACRX_T("\n Number of hits wanted: "), &hitCount);
// Query the line type
const AcGeLinearEnt3d* line = NULL;
ACHAR opt[128];
while (Adesk::kTrue) {
acutPrintf(ACRX_T("\nEnter Line Type: "));
acedInitGet(NULL, ACRX_T("Infinite Ray Segment"));
if (acedGetKword(ACRX_T("Infinite/Ray/<Segment>: "), opt) == RTCAN) return;
// Map the user input to a valid line type
if ((_tcscmp(opt, ACRX_T("Segment")) == 0) || (_tcscmp(opt, ACRX_T("")) == 0)) {
line = new AcGeLineSeg3d(startPt, endPt);
break;
} else if (_tcscmp(opt, ACRX_T("Ray")) == 0) {
line = new AcGeRay3d(startPt,endPt);
break;
} else if (_tcscmp(opt, ACRX_T("Infinite")) == 0) {
line = new AcGeLine3d(startPt, endPt);
break;
}
}
if (line == NULL) {
acutPrintf(ACRX_T("\n lineContainment: unable to allocate memory for line\n"));
delete pEnt;
return;
}
Adesk::UInt32 numHitsWanted = (Adesk::UInt32)hitCount;
Adesk::UInt32 numHitsFound = 0;
AcBrHit* hits = NULL;
returnValue = pEnt->getLineContainment(*line, numHitsWanted, numHitsFound, hits);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in AcBrEntity::getLineContainment:"));
errorReport(returnValue);
delete pEnt;
return;
}
acutPrintf(ACRX_T("\n Number of hits found: %d"), numHitsFound);
for (Adesk::UInt32 i = 0; i < numHitsFound; i++) {
AcBrEntity* entityAssociated = NULL;
returnValue = hits[i].getEntityAssociated(entityAssociated);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in AcBrHit::getEntityAssociated:"));
errorReport(returnValue);
delete entityAssociated;
break;
}
if (!pEnt->isEqualTo(entityAssociated)) {
acutPrintf(ACRX_T("\n lineContainment: Hit owner is not the entity we checked line containment against!"));
delete entityAssociated;
break;
}
AcGePoint3d pt;
returnValue = hits[i].getPoint(pt);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in AcBrHit::getPoint:"));
errorReport(returnValue);
break;
}
AcBrEntity* entityHit = NULL;
returnValue = hits[i].getEntityHit(entityHit);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in AcBrHit::getEntityHit:"));
errorReport(returnValue);
delete entityHit;
break;
}
AcBrEntity* entityEntered = NULL;
returnValue = hits[i].getEntityEntered(entityEntered);
if (returnValue != AcBr::eOk) {
acutPrintf(ACRX_T("\n Error in AcBrHit::getEntityEntered:"));
errorReport(returnValue);
delete entityHit;
delete entityEntered;
break;
}
lnContainmentReport(i, pt, entityHit, entityEntered);
delete entityHit;
delete entityEntered;
}
delete pEnt;
delete[] hits;
return;
} |
|