|
How to Transform geometry data between model and paper space using ObjectARX/.Net?
Issue
How do I convert geometry data from paper space to model space for a particular
viewport?
Solution :
One approach is to use the function acedTrans (see the documentation for its
usage). Another is to use the data directly from the AcDbViewport. The
following code converts data from paper space in the corresponding viewport to
model space. The opposite conversion can be done by inverting the matrix
calculated.
/*Code*/
//////////////////////////////////////////////////////////////////////////
// model space to paper space
static void ms2ps(AcDbViewport*pVp, AcGeMatrix3d &resultMat);
// visa versa
static void ps2ms(AcDbViewport*pVp, AcGeMatrix3d &resultMat);
//////////////////////////////////////////////////////////////////////////
// This is command 'ALIGN, by Fenton Webb [12/10/2004], DevTech, Autodesk
void asdkUTILSAlign()
{
// First select a viewport to calculate the transformation for
ads_name eName;
ads_point pt;
if( RTNORM != acedEntSel("\nPlease pick a viewport", eName, pt ))
return;
// Get The Id
AcDbObjectId id;
acdbGetObjectId(id, eName);
// Open up the viewport
AcDbObjectPointer<AcDbViewport> pVp (id, AcDb::kForRead);
// if ok
if (pVp.openStatus() == Acad::eOk)
{
// Open the ModelSpace Record
AcDbBlockTableRecordPointer pMsRecord (ACDB_MODEL_SPACE, curDoc()->database(), AcDb::kForWrite);
// if ok
if (pMsRecord.openStatus() == Acad::eOk)
{
// Pick a line
if( RTNORM != acedEntSel( "\nPlease select Paper space line\n", eName, pt ) )
return;
acdbGetObjectId( id, eName );
// Get the projection matrix
AcGeMatrix3d ms2PsMat;
ps2ms(pVp.object(), ms2PsMat);
// Open the line
AcDbObjectPointer<AcDbLine> pPsLine (id, AcDb::kForRead);
// if ok
if (pPsLine.openStatus() == Acad::eOk)
{
AcDbEntity *pMsEntity = NULL;
// Now get a transformed copy
pPsLine->getTransformedCopy( ms2PsMat, pMsEntity);
if(pMsEntity != NULL )
{
// create a smart pointer
AcDbObjectPointer<AcDbEntity> pMsEntitySmartPtr;
// acquire the newly created pointer - now we don't need to worry about closing
pMsEntitySmartPtr.acquire(pMsEntity);
// Change its color, so we can see the new entity!
pMsEntitySmartPtr->setColorIndex(2);
// now add it to model space
pMsRecord->appendAcDbEntity(pMsEntitySmartPtr.object());
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void ms2ps( AcDbViewport*pVp, AcGeMatrix3d &resultMat)
{
// first get all the data
AcGeVector3d viewDirection = pVp->viewDirection();
AcGePoint2d centre2d = pVp->viewCenter();
AcGePoint3d viewCenter = AcGePoint3d( centre2d.x, centre2d.y, 0);
AcGePoint3d viewTarget = pVp->viewTarget ();
double twistAngle = -pVp->twistAngle();
AcGePoint3d centerPoint = pVp->centerPoint();
double viewHeight = pVp->viewHeight();
double height = pVp->height();
double width = pVp->width();
double scaling = viewHeight / height;
double lensLength = pVp->lensLength();
// prepare the transformation
AcGeVector3d xAxis, yAxis, zAxis;
zAxis = viewDirection.normal();
xAxis = AcGeVector3d::kZAxis.crossProduct( viewDirection );
if( !xAxis.isZeroLength() ) {
xAxis.normalize();
yAxis = zAxis.crossProduct( xAxis );
} else if( zAxis.z < 0 ) {
xAxis = -AcGeVector3d::kXAxis;
yAxis = AcGeVector3d::kYAxis;
zAxis = -AcGeVector3d::kZAxis;
} else {
xAxis = AcGeVector3d::kXAxis;
yAxis = AcGeVector3d::kYAxis;
zAxis = AcGeVector3d::kZAxis;
}
AcGeMatrix3d dcs2wcs; // display coordinate system (DCS) to world coordinate system (WCS)
AcGeMatrix3d ps2Dcs; // paperspace to DCS
// First initialise with a transformation to centerPoint
ps2Dcs = AcGeMatrix3d::translation( AcGePoint3d::kOrigin - centerPoint);
// then scale for the view
ps2Dcs = ps2Dcs * AcGeMatrix3d::scaling( scaling, centerPoint);
// then adjust to the viewCenter
dcs2wcs = AcGeMatrix3d::translation( viewCenter - AcGePoint3d::kOrigin);
// Then transform for the view direction
AcGeMatrix3d matCoords;
matCoords.setCoordSystem( AcGePoint3d::kOrigin, xAxis, yAxis, zAxis);
dcs2wcs = matCoords * dcs2wcs;
// Then adjust for the viewTarget
dcs2wcs = AcGeMatrix3d::translation( viewTarget - AcGePoint3d::kOrigin) * dcs2wcs;
// Then the twist angle
dcs2wcs = AcGeMatrix3d::rotation(twistAngle, zAxis, viewTarget ) *dcs2wcs;
AcGeMatrix3d perspMat;
if( pVp->isPerspectiveOn())
{
// we do special perspective handling
double viewSize = viewHeight;
double aspectRatio = width / height;
double adjustFactor = 1.0 / 42.0;
double adjustedLensLength = viewSize * lensLength * sqrt ( 1.0 +
aspectRatio * aspectRatio ) * adjustFactor;
double eyeDistance = viewDirection.length();
double lensDistance = eyeDistance - adjustedLensLength;
double ed = eyeDistance;
double ll = adjustedLensLength;
double l = lensDistance;
perspMat.entry[2][2] = (ll - l ) / ll;
perspMat.entry[2][3] = l * ( ed - ll ) / ll;
perspMat.entry[3][2] = -1.0 / ll;
perspMat.entry[3][3] = ed / ll;
}
resultMat = ps2Dcs.inverse() * perspMat * dcs2wcs.inverse();
}
//////////////////////////////////////////////////////////////////////////
void ps2ms ( AcDbViewport*pVp, AcGeMatrix3d &resultMat )
{
// Keep life simple, just invert the other way
AcGeMatrix3d mat;
ms2ps( pVp, mat );
resultMat = mat.inverse();
return;
}
Here is a C# version of this code:
static Matrix3d ms2ps(Viewport pVp)
{
// first get all the data
Vector3d viewDirection = pVp.ViewDirection;
Point3d viewCenter = new Point3d(pVp.ViewCenter.X, pVp.ViewCenter.Y, 0);
Point3d viewTarget = pVp.ViewTarget;
Point3d centerPoint = pVp.CenterPoint;
double twistAngle = -pVp.TwistAngle;
double viewHeight = pVp.ViewHeight;
double height = pVp.Height;
double width = pVp.Width;
double scaling = viewHeight / height;
double lensLength = pVp.LensLength;
// prepare the transformation
Vector3d zAxis = viewDirection.GetNormal();
Vector3d xAxis = Vector3d.ZAxis.CrossProduct(viewDirection);
Vector3d yAxis;
if (!xAxis.IsZeroLength())
{
xAxis = xAxis.GetNormal();
yAxis = zAxis.CrossProduct(xAxis);
}
else if (zAxis.Z < 0)
{
xAxis = Vector3d.XAxis * -1;
yAxis = Vector3d.YAxis;
zAxis = Vector3d.ZAxis * -1;
}
else
{
xAxis = Vector3d.XAxis;
yAxis = Vector3d.YAxis;
zAxis = Vector3d.ZAxis;
}
Matrix3d dcs2wcs; // display coordinate system (DCS) to world coordinate system (WCS)
Matrix3d ps2Dcs; // paperspace to DCS
// First initialise with a transformation to centerPoint
ps2Dcs = Matrix3d.Displacement(Point3d.Origin - centerPoint);
// then scale for the view
ps2Dcs = ps2Dcs * Matrix3d.Scaling(scaling, centerPoint);
// then adjust to the viewCenter
dcs2wcs = Matrix3d.Displacement(viewCenter - Point3d.Origin);
// Then transform for the view direction
Matrix3d matCoords = Matrix3d.AlignCoordinateSystem(Point3d.Origin,
Vector3d.XAxis,
Vector3d.YAxis,
Vector3d.ZAxis,
Point3d.Origin,
xAxis,
yAxis,
zAxis);
dcs2wcs = matCoords * dcs2wcs;
// Then adjust for the viewTarget
dcs2wcs = Matrix3d.Displacement(viewTarget - Point3d.Origin) * dcs2wcs;
// Then the twist angle
dcs2wcs = Matrix3d.Rotation(twistAngle, zAxis, viewTarget) * dcs2wcs;
Matrix3d perspMat = Matrix3d.Identity;
if (pVp.PerspectiveOn)
{
// we do special perspective handling
double viewSize = viewHeight;
double aspectRatio = width / height;
double adjustFactor = 1.0 / 42.0;
double adjustedLensLength = viewSize * lensLength * Math.Sqrt(1.0 + aspectRatio * aspectRatio) * adjustFactor;
double eyeDistance = viewDirection.Length;
double lensDistance = eyeDistance - adjustedLensLength;
double ed = eyeDistance;
double ll = adjustedLensLength;
double l = lensDistance;
double[] data = new double[] {1,0,0,0,
0,1,0,0,
0,0, (ll - l ) / ll, l * ( ed - ll ) / ll,
0,0, -1.0/ll, ed/ll};
perspMat = new Matrix3d(data);
}
Matrix3d resultMat = ps2Dcs.Inverse() * perspMat * dcs2wcs.Inverse();
return resultMat;
}
[CommandMethod("ConvertModel2Paper")]
static public void ConvertModel2Paper()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions peo = new PromptEntityOptions("\nSelect a viewport: ");
peo.SetRejectMessage("\nMust be a viewport...");
peo.AddAllowedClass(typeof(Viewport), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
using (Transaction Tx = db.TransactionManager.StartTransaction())
{
Viewport vp = Tx.GetObject(per.ObjectId, OpenMode.ForRead) as Viewport;
Matrix3d Transfo = ms2ps(vp);
BlockTable bT = Tx.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btrModel = Tx.GetObject(bT[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
BlockTableRecord btrPaper = Tx.GetObject(bT[BlockTableRecord.PaperSpace], OpenMode.ForWrite) as BlockTableRecord;
foreach (ObjectId id in btrModel)
{
Entity entity = Tx.GetObject(id, OpenMode.ForRead) as Entity;
Entity paperEntity = entity.Clone() as Entity;
paperEntity.TransformBy(Transfo);
paperEntity.ColorIndex = 1;
btrPaper.AppendEntity(paperEntity);
Tx.AddNewlyCreatedDBObject(paperEntity, true);
}
Tx.Commit();
}
}
static Matrix3d ps2ms(Viewport pVp)
{
Matrix3d mat = ms2ps(pVp);
return mat.Inverse();
}
[CommandMethod("ConvertPaper2Model")]
static public void ConvertPaper2Model()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions peo = new PromptEntityOptions("\nSelect a viewport: ");
peo.SetRejectMessage("\nMust be a viewport...");
peo.AddAllowedClass(typeof(Viewport), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
using (Transaction Tx = db.TransactionManager.StartTransaction())
{
Viewport vp = Tx.GetObject(per.ObjectId, OpenMode.ForWrite) as Viewport;
Matrix3d ps2msTransfo = ps2ms(vp);
BlockTable b = Tx.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = Tx.GetObject(b[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
//Boundary
Point3d p1 = new Point3d(vp.CenterPoint.X - vp.Width * 0.5, vp.CenterPoint.Y - vp.Height * 0.5, 0);
Point3d p2 = new Point3d(vp.CenterPoint.X - vp.Width * 0.5, vp.CenterPoint.Y + vp.Height * 0.5, 0);
Point3d p3 = new Point3d(vp.CenterPoint.X + vp.Width * 0.5, vp.CenterPoint.Y + vp.Height * 0.5, 0);
Point3d p4 = new Point3d(vp.CenterPoint.X + vp.Width * 0.5, vp.CenterPoint.Y - vp.Height * 0.5, 0);
Line l1 = new Line(p1, p2);
Line l2 = new Line(p2, p3);
Line l3 = new Line(p3, p4);
Line l4 = new Line(p4, p1);
l1.TransformBy(ps2msTransfo);
l2.TransformBy(ps2msTransfo);
l3.TransformBy(ps2msTransfo);
l4.TransformBy(ps2msTransfo);
btr.AppendEntity(l1);
btr.AppendEntity(l2);
btr.AppendEntity(l3);
btr.AppendEntity(l4);
Tx.AddNewlyCreatedDBObject(l1, true);
Tx.AddNewlyCreatedDBObject(l2, true);
Tx.AddNewlyCreatedDBObject(l3, true);
Tx.AddNewlyCreatedDBObject(l4, true);
//Paper Space Entity
peo = new PromptEntityOptions("\nSelect a paper space line: ");
peo.SetRejectMessage("\nMust be a line...");
peo.AddAllowedClass(typeof(Line), true);
per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK) return;
Line line = Tx.GetObject(per.ObjectId, OpenMode.ForRead) as Line;
Entity clonedLine = line.GetTransformedCopy(ps2msTransfo);
//Set color to red so we can reconize it easily
clonedLine.ColorIndex = 1;
btr.AppendEntity(clonedLine);
Tx.AddNewlyCreatedDBObject(clonedLine, true);
Tx.Commit();
}
}
/*Code*/ |
|