When working with viewports in AutoCAD, particularly when translating between paper space and model space, it is often necessary to determine the viewport boundaries within the model space. This blog post will guide you through obtaining viewport extents and transforming them appropriately.
Understanding the Viewport Transformation
AutoCAD viewports in paper space define a window into model space. To retrieve the viewport bounds in model space, we need to apply a transformation matrix that accounts for scaling, rotation, and translation.
Key Steps:
-
Retrieve viewport properties – Extract viewport size, center, and transformation parameters.
-
Compute transformation matrix – Convert from Paper Space Display Coordinate System (PSDCS) to the World Coordinate System (WCS).
-
Transform viewport extents – Apply the matrix to get accurate model space boundaries.
-
Create a bounding polyline – Draw a rectangle representing the viewport bounds.
Implementation
Below is the C++ implementation using ObjectARX APIs to extract the viewport bounds in model space.
Setting Viewport Properties
static void SetViewportProperties(AcDbViewport* pVp,
AcDbViewTableRecord& vwRec)
{
vwRec.setHeight(pVp->height());
vwRec.setWidth(pVp->width());
vwRec.setCenterPoint(AcGePoint2d(pVp->centerPoint().x,
pVp->centerPoint().y));
vwRec.setViewDirection(pVp->viewDirection());
vwRec.setTarget(pVp->viewTarget());
vwRec.setLensLength(pVp->lensLength());
vwRec.setViewTwist(pVp->twistAngle());
vwRec.setFrontClipDistance(pVp->frontClipDistance());
vwRec.setBackClipDistance(pVp->backClipDistance());
vwRec.setPerspectiveEnabled(pVp->isPerspectiveOn());
vwRec.setFrontClipEnabled(pVp->isFrontClipOn());
vwRec.setBackClipEnabled(pVp->isBackClipOn());
vwRec.setFrontClipAtEye(pVp->isFrontClipAtEyeOn());
vwRec.setViewAssociatedToViewport(pVp->isUcsSavedWithViewport());
vwRec.setVisualStyle(pVp->visualStyleId());
vwRec.setIsPaperspaceView(true);
}
Computing the Transformation Matrix
static AcGeMatrix3d GetTransformationMatrix(AcDbViewport* pVpPtr) {
if (!pVpPtr) return AcGeMatrix3d::kIdentity;
// Extract viewport properties
AcGePoint3d viewCenterPSDCS(pVpPtr->viewCenter().x, pVpPtr->viewCenter().y, 0.0);
AcGePoint3d viewCenterDCS = pVpPtr->centerPoint();
double scale = 1.0 / pVpPtr->customScale();
// Transform from PSDCS to DCS
AcGeMatrix3d transformPSDCSToDCS = AcGeMatrix3d::scaling(scale, viewCenterDCS) *
AcGeMatrix3d::translation(viewCenterDCS - viewCenterPSDCS);
// Retrieve view properties
AcDbViewTableRecord vwRec;
SetViewportProperties(pVpPtr, vwRec);
AcGeVector3d targetDirection = vwRec.target() - AcGePoint3d::kOrigin;
AcGeMatrix3d translation = AcGeMatrix3d::translation(targetDirection);
double twistAngle = vwRec.viewTwist();
AcGeVector3d viewDirection = vwRec.viewDirection();
AcGePoint3d pointOfRotation = vwRec.target();
AcGeMatrix3d rotation = AcGeMatrix3d::rotation(-twistAngle,
viewDirection,
pointOfRotation);
// Transform from DCS to WCS
AcGeMatrix3d transformDCSToWCS = AcGeMatrix3d::planeToWorld(viewDirection)
* translation
* rotation;
return transformDCSToWCS * transformPSDCSToDCS;
}
Retrieving Viewport Bounds in Model Space
Once we have the transformation matrix, we can apply it to the viewport extents to get the bounds in model space.
static AcDbExtents GetViewportBoundsInMS(AcDbViewport* pVp) {
AcGeMatrix3d xform = GetTransformationMatrix(pVp);
AcDbExtents vpExtents;
pVp->getGeomExtents(vpExtents);
AcGePoint3d minPt = vpExtents.minPoint();
AcGePoint3d maxPt = vpExtents.maxPoint();
vpExtents.transformBy(xform);
return vpExtents;
}
Test Method
Finally, we can create a test method or run through a Command to execute the above functions and visualize the viewport bounds.
void runGetVpBoundsInMS() {
auto workingDb = acdbHostApplicationServices()->workingDatabase();
if (workingDb->tilemode() == Adesk::kTrue) {
acutPrintf(_T("\nTilemode is on! Execute in PAPERSPACE"));
return;
}
AcDbObjectPointer <AcDbViewport> pViewportPtr(workingDb->paperSpaceVportId(),
AcDb::kForRead);
if (!eOkVerify(pViewportPtr.openStatus())) return;
AcDbObjectPointer <AcDbLayout> pLayoutPtr(workingDb->currentLayoutId(), AcDb::kForRead);
if (!eOkVerify(pLayoutPtr.openStatus())) return;
AcDbObjectIdArray vpIds = pLayoutPtr->getViewportArray();
if (vpIds.length() < 2) {
acutPrintf(_T("\nNo viewports found in paperspace"));
return;
}
AcDbObjectPointer <AcDbViewport> pVpPtr(vpIds[1], AcDb::kForRead);
if (!eOkVerify(pVpPtr.openStatus())) return;
AcDbExtents vpExtents = GetViewportBoundsInMS(pVpPtr.object());
AcGePoint3d minPt = vpExtents.minPoint();
AcGePoint3d maxPt = vpExtents.maxPoint();
AcDbPolyline* pPoly = new AcDbPolyline(4);
pPoly->addVertexAt(0, AcGePoint2d(minPt.x, minPt.y));
pPoly->addVertexAt(1, AcGePoint2d(maxPt.x, minPt.y));
pPoly->addVertexAt(2, AcGePoint2d(maxPt.x, maxPt.y));
pPoly->addVertexAt(3, AcGePoint2d(minPt.x, maxPt.y));
pPoly->setClosed(true);
pPoly->setColorIndex(3);
addToDb(pPoly, workingDb);
}
Here is video