000

Index Labels

Open A Viewport From ModelSpace

.
I posted an article on selecting entities in ModelSpace that are visible from a PaperSpace Viewport a while ago. The key point of that article is to identify an area in ModelSpace that is visible in a "Display-Locked" Viewport in PaperSpace.

While writing that article, I was thinking it would be another topic that I would write on: opening a Viewport based on selected area in ModelSpace.

When presenting content in ModelSpace on a layout, user usually:
  • Set a selected layout current;
  • Open a Viewport;
  • Activate the Viewport into with "MSpace";
  • Twist the view angle if necessary;
  • Scale the view in the Viewport;
  • Pan the view in the Viewport to fit the desired drawing content to be shown in the Viewport;
  • Finally, lock the Viewport's display.
I was thinking that the process can be simplified like this:
  • In ModelSpace, user select an area with a polygon that is to be shown in a PaperSpace Viewport. It would be similar to AutoCAD's polygon selection;
  • User select an angle that the view would be twisted in Viewport;
  • User specify the view scale in Viewport;
  • A Viewport will be opened on given layout that scaled and twisted properly to fit the selected area/polygon, which encloses the drawing content to be shown in the opened Viewport.
As you can see, since the user usually already knows the view scale and the view twist angle when he/she is ready to present drawing content in Viewports on layout, the proposed process means that user just need to specify what area to be shown, a Viewport would be opened with proper size, scale and twist angle.

With this topic hanging in my mind for a few months, I finally found a bit time to actually write some code to see it in action.

First, I defined a class ViewportBoundaryInfo that represents a rectangle Viewport being projected into ModelSpace:

using Autodesk.AutoCAD.Geometry;

namespace OpenViewportFromModelSpace
{
    public class ViewportBoundaryInfo
    {
        public Point3d Centre
        {
            get
            {
                double x =
                    (PointLL.X + PointLU.X + PointRL.X + PointRU.X) / 4.0;
                double y =
                    (PointLL.Y + PointLU.Y + PointRL.Y + PointRU.Y) / 4.0;
                double z =
                    (PointLL.Z + PointLU.Z + PointRL.Z + PointRU.Z) / 4.0;

                return new Point3d(x, y, z);
            }
        }
        public Point3d PointLL { setget; }

        public Point3d PointLU { setget; }

        public Point3d PointRU { setget; }

        public Point3d PointRL { setget; }

        public double Rotation { setget; }

        public double Width
        {
            get { return PointLL.DistanceTo(PointRL); }
        }

        public double Height
        {
            get { return PointLL.DistanceTo(PointLU); }
        }

        public Point3dCollection RectanglePointsInModel
        {
            get
            {
                Point3dCollection points = new Point3dCollection();
                points.Add(PointLL);
                points.Add(PointRL);
                points.Add(PointRU);
                points.Add(PointLU);
                return points;
            }
        }

        public double TwistAngle { setget; }

        public ViewportBoundaryInfo()
        {
            PointLL = new Point3d();
            PointLU = new Point3d();
            PointRL = new Point3d();
            PointRU = new Point3d();
            TwistAngle = 0.0;
        }
    }

}

Then I decided to design a class that collect user input on which drawing area in ModelSpace to be shown in a Viewport. As the result of user input, the boundary of Viewport to be created would be the output. User also get chance to see the boundary rectangle before proceeding to actually create the Viewport. Here is the class OpenViewportVisualHelper:

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using CadDb = Autodesk.AutoCAD.DatabaseServices;

namespace OpenViewportFromModelSpace
{
    public class OpenViewportVisualHelper : IDisposable 
    {
        private Document _dwg;
        private Editor _ed;
        private Database _db;
        private TransientManager _tsManager;

        private ViewportBoundaryInfo _vpBoundaryInfo = null;

        private Point3dCollection _polygonPoints = null;
        private CadDb.Polyline _polygon = null;
        private CadDb.Polyline _rectangle = null;
        public OpenViewportVisualHelper(Document dwg)
        {
            _dwg=dwg;
            _ed=dwg.Editor;
            _db=dwg.Database;
            _tsManager = TransientManager.CurrentTransientManager;
        }

        public ViewportBoundaryInfo ViewportBoundaryInfo
        {
            get { return _vpBoundaryInfo; }
        }

        public bool PickEnclosingPolygon()
        {
            ViewportBoundaryInfo vportInfo = new ViewportBoundaryInfo();

            if (!PickEnclosingPoints()) return false;

            if (_polygon != null)
            {
                double twistAngle;
                if (PickTwistAngle(_ed, out twistAngle))
                {
                    Point3d pLL, pRL, pRU, pLU;
                    GetTwistedBoundingBox(_polygon, twistAngle,
                        out pLL, out pRL, out pRU, out pLU);

                    vportInfo.PointLL = pLL;
                    vportInfo.PointRL = pRL;
                    vportInfo.PointRU = pRU;
                    vportInfo.PointLU = pLU;

                    vportInfo.TwistAngle = twistAngle;
                }

                _vpBoundaryInfo = vportInfo;

                DrawEnclosingPolygon(_polygonPoints);
                DrawViewportRectangle(vportInfo.RectanglePointsInModel);
                _ed.UpdateScreen();

                PromptKeywordOptions opt = new PromptKeywordOptions(
                    "\nDraw viewport boundary in ModelSpace permanently?");
                opt.AppendKeywordsToMessage = true;
                opt.Keywords.Add("Yes");
                opt.Keywords.Add("No");
                opt.Keywords.Default = "Yes";
                PromptResult res = _ed.GetKeywords(opt);
                bool ok = true;
                if (res.Status == PromptStatus.OK)
                {
                    if (res.StringResult == "Yes")
                    {
                        AddRectangleToModelSpace(_rectangle);
                    }
                }
                else
                {
                    ok = false;
                }

                _tsManager.EraseTransient(
                    _polygon, new IntegerCollection());
                _tsManager.EraseTransient(
                    _rectangle, new IntegerCollection());

                _polygon.Dispose();
                _rectangle.Dispose();

                return ok;
            }
            else
            {
                return false;
            }
        }

        public void Dispose()
        {
            if (_polygon!=null)
            {
                _tsManager.EraseTransient(_polygon, new IntegerCollection());
                _polygon.Dispose();
            }

            if (_rectangle!=null)
            {
                _tsManager.EraseTransient(_rectangle, new IntegerCollection());
                _rectangle.Dispose();
            }
        }

        #region private methods

        private bool PickEnclosingPoints()
        {
            bool ok = true;
            _polygonPoints = new Point3dCollection();

            _ed.PointMonitor += MyPointMonitor;

            while(true)
            {
                string msg;
                PromptPointOptions opt;

                if (_polygonPoints.Count == 0)
                {
                    msg = "Select first point of enclosing polyline:";
                    opt = new PromptPointOptions("\n" + msg);
                }
                else if (_polygonPoints.Count == 1)
                {
                    msg = "Select second point of enclosing polyline:";
                    opt = new PromptPointOptions("\n" + msg);
                    opt.UseBasePoint = true;
                    opt.BasePoint = _polygonPoints[0];
                    opt.UseDashedLine = true;
                }
                else
                {
                    msg = "select next point of enclosing polyline:";
                    opt = new PromptPointOptions("\n" + msg);
                }

                if (_polygonPoints.Count > 2)
                {
                    opt.Keywords.Add("Done");
                    opt.Keywords.Default = "Done";
                    opt.AllowNone = true;
                }

                PromptPointResult res = _ed.GetPoint(opt);
                if (res.Status==PromptStatus.OK || res.Status==PromptStatus.Keyword)
                {
                    if (res.Status == PromptStatus.OK)
                    {
                        _polygonPoints.Add(res.Value);
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    ok = false;
                    break;
                }
            }

            _ed.PointMonitor -= MyPointMonitor;

            DrawEnclosingPolygon(_polygonPoints);

            return ok;
        }

        private void MyPointMonitor(object sender, PointMonitorEventArgs e)
        {
            Point3dCollection points = new Point3dCollection();
            foreach (Point3d p in _polygonPoints)
            {
                points.Add(p);
            }
            points.Add(e.Context.RawPoint);
            DrawEnclosingPolygon(points);
        }

        private void DrawEnclosingPolygon(Point3dCollection points)
        {
            if (_polygon != null)
            {
                _tsManager.EraseTransient(_polygon, new IntegerCollection());
                _polygon.Dispose();
                _polygon = null;
            }

            _polygon = CreatePolygonFromPoints(points);
            _tsManager.AddTransient(
                _polygon, 
                TransientDrawingMode.DirectTopmost, 
                128, 
                new IntegerCollection());
        }

        private void DrawViewportRectangle(Point3dCollection points)
        {
            if (_rectangle!=null)
            {
                _tsManager.EraseTransient(_rectangle, new IntegerCollection());
                _rectangle.Dispose();
                _rectangle = null;
            }

            _rectangle = CreatePolygonFromPoints(points);
            _tsManager.AddTransient(
                _rectangle, 
                TransientDrawingMode.DirectTopmost, 
                128, 
                new IntegerCollection());
        }

        private CadDb.Polyline CreatePolygonFromPoints(Point3dCollection points)
        {
            CadDb.Polyline pl = new CadDb.Polyline(points.Count);

            for (int i = 0; i < points.Count; i++ )
            {
                pl.AddVertexAt(
                    i, new Point2d(points[i].X, points[i].Y), 0.0, 0.0, 0.0);
            }

            pl.ColorIndex = 2;
            if (pl.NumberOfVertices > 2) pl.Closed = true;
             
            return pl;
        }

        private bool PickTwistAngle(Editor ed, out double twistAngle)
        {
            twistAngle = 0.0;

            Point3d pt1, pt2;

            ed.WriteMessage(
                "\nDetermine viewport twist angle by " +
                "selecting 2 points as a horizontal line in viewport.");
            PromptPointOptions opt = new PromptPointOptions(
                "\nSelect first point:");

            PromptPointResult res = ed.GetPoint(opt);
            if (res.Status == PromptStatus.OK)
            {
                pt1 = res.Value;

                opt = new PromptPointOptions("\nSelect second point:");
                opt.UseBasePoint = true;
                opt.UseDashedLine = true;
                opt.BasePoint = pt1;

                res = ed.GetPoint(opt);
                if (res.Status == PromptStatus.OK)
                {
                    pt2 = res.Value;

                    //We can calculate the angle based on the 2 points
                    //Being lazy, I use a line to get the angle
                    using (CadDb.Line line=new CadDb.Line(pt1,pt2))
                    {
                        twistAngle = line.Angle;
                    }

                    return true;
                }
            }

            return false;
        }

        private void GetTwistedBoundingBox(
            CadDb.Polyline pl, double twistAngle,
            out Point3d pointLL, out Point3d pointRL,
            out Point3d pointRU, out Point3d pointLU)
        {

            Point3d pt = pl.GetPoint3dAt(0);
            double ang = Math.PI * 2.0 - twistAngle;
            Matrix3d mt = Matrix3d.Rotation(ang, Vector3d.ZAxis, pt);

            //Clone the polyline
            CadDb.Polyline clone = pl.Clone() as CadDb.Polyline;
            using (clone)
            {
                clone.TransformBy(mt);
                Extents3d ext = clone.GeometricExtents;
                pointLL = (new Point3d(ext.MinPoint.X, ext.MinPoint.Y, 0.0))
                    .TransformBy(mt.Inverse());
                pointRL = (new Point3d(ext.MaxPoint.X, ext.MinPoint.Y, 0.0))
                    .TransformBy(mt.Inverse());
                pointRU = (new Point3d(ext.MaxPoint.X, ext.MaxPoint.Y, 0.0))
                    .TransformBy(mt.Inverse());
                pointLU = (new Point3d(ext.MinPoint.X, ext.MaxPoint.Y, 0.0))
                    .TransformBy(mt.Inverse());
            }
        }

        private void AddRectangleToModelSpace(CadDb.Polyline pl)
        {
            using (Transaction tran=
                _dwg.TransactionManager.StartTransaction())
            {
                BlockTableRecord model = (BlockTableRecord)tran.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(_db),
                    OpenMode.ForWrite);
                model.AppendEntity(pl);
                tran.AddNewlyCreatedDBObject(pl, true);
                tran.Commit();
            }
        }

        #endregion
    }
}

Since I used TransientGraphics to show the desired drawing content to be shown in the Viewport (the selecting polygon) and the Viewport boundary rectangle, thus temporary, non-database-residing entity (Polyline) is used. So, I implemented IDispose interface with this class. This way, I can use much simple "using {...}" block to make sure the temporary entity being disposed.

Now that I can correctly capture user input on what portion of drawing content in ModelSpace to be displayed in a Viewport, I can then write some code to open a Viewport on specific layout. The Viewport would show exactly the rectangle Viewport boundary the user wants. Here is the code in class CadHelper:

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace OpenViewportFromModelSpace
{
public class CadHelper
{
public static string SelectLayout()
{
Document dwg = CadApp.DocumentManager.MdiActiveDocument;
Editor ed = dwg.Editor;

PromptStringOptions opt=new PromptStringOptions(
"\nEnter the name of a layout, " +
"where viewport will be created: ");
opt.AllowSpaces = true;

PromptResult res = ed.GetString(opt);
if (res.Status == PromptStatus.OK)
{
return res.StringResult;
}
else
{
return null;
}
}

public static double GetPaperToModelScale()
{
Document dwg = CadApp.DocumentManager.MdiActiveDocument;
Editor ed = dwg.Editor;

PromptDoubleOptions opt = new PromptDoubleOptions(
"\nEnter PaperSpace scale for the viewport:");
opt.AllowNegative = false;
opt.AllowZero = false;
opt.AllowNone = false;
opt.AllowArbitraryInput = false;
opt.DefaultValue = 1.0;
opt.UseDefaultValue = true;

PromptDoubleResult res = ed.GetDouble(opt);
if (res.Status==PromptStatus.OK)
{
return res.Value;
}
else
{
return double.MinValue;
}
}

public static ObjectId CreateRectangularViewport(
string layout, string layer, 
double paperToModelScale,
ViewportBoundaryInfo vportInfo)
{
//Create a vewport in the center of the layout
Document dwg=CadApp.DocumentManager.MdiActiveDocument;
ObjectId vpId = OpenViewport(
dwg, 
layout, 
layer, 
vportInfo.TwistAngle, 
paperToModelScale, 
vportInfo.Centre);

//Resize the viewport
ResizeViewport(dwg, vpId, vportInfo, paperToModelScale);

return ObjectId.Null;
}

#region code borrowed from www.theswamp.org
//**********************************************************************
//Create coordinate transform matrix 
//between modelspace and paperspace viewport

//The code is borrowed from
//http://www.theswamp.org/index.php?topic=34590.msg398539#msg398539
//*********************************************************************
public static Matrix3d PaperToModel(Viewport vp)
{
Matrix3d mx = ModelToPaper(vp);
return mx.Inverse();
}

public static Matrix3d ModelToPaper(Viewport vp)
{
Vector3d vd = vp.ViewDirection;
Point3d vc = new Point3d(vp.ViewCenter.X, vp.ViewCenter.Y, 0);
Point3d vt = vp.ViewTarget;
Point3d cp = vp.CenterPoint;
double ta = -vp.TwistAngle;
double vh = vp.ViewHeight;
double height = vp.Height;
double width = vp.Width;
double scale = vh / height;
double lensLength = vp.LensLength;
Vector3d zaxis = vd.GetNormal();
Vector3d xaxis = Vector3d.ZAxis.CrossProduct(vd);
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 pcsToDCS = Matrix3d.Displacement(Point3d.Origin - cp);
pcsToDCS = pcsToDCS * Matrix3d.Scaling(scale, cp);
Matrix3d dcsToWcs = Matrix3d.Displacement(vc - Point3d.Origin);
Matrix3d mxCoords = Matrix3d.AlignCoordinateSystem(
Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, 
                Vector3d.ZAxis, Point3d.Origin,
xaxis, yaxis, zaxis);
dcsToWcs = mxCoords * dcsToWcs;
dcsToWcs = Matrix3d.Displacement(vt - Point3d.Origin) * dcsToWcs;
dcsToWcs = Matrix3d.Rotation(ta, zaxis, vt) * dcsToWcs;

Matrix3d perspectiveMx = Matrix3d.Identity;
if (vp.PerspectiveOn)
{
double vSize = vh;
double aspectRatio = width / height;
double adjustFactor = 1.0 / 42.0;
double adjstLenLgth = vSize * lensLength *
Math.Sqrt(1.0 + aspectRatio * aspectRatio) * adjustFactor;
double iDist = vd.Length;
double lensDist = iDist - adjstLenLgth;
                 double[] dataAry = new double[] 
                     {   
                         1,0,0,0,0,1,0,0,0,0,
    (adjstLenLgth-lensDist)/adjstLenLgth,
                         lensDist*(iDist-adjstLenLgth)/adjstLenLgth,
    0,0,-1.0/adjstLenLgth,iDist/adjstLenLgth
                     };

perspectiveMx = new Matrix3d(dataAry);
}

Matrix3d finalMx = 
                pcsToDCS.Inverse() * perspectiveMx * dcsToWcs.Inverse();

return finalMx;
}

#endregion

#region private methods

private static void GetTwistedBoundingBox(
Polyline pl, double twistAngle,
out Point3d pointLL, out Point3d pointRL, 
out Point3d pointRU, out Point3d pointLU)
{

Point3d pt = pl.GetPoint3dAt(0);
double ang = Math.PI * 2.0 - twistAngle;
Matrix3d mt = Matrix3d.Rotation(ang, Vector3d.ZAxis, pt);

//Clone the polyline
Polyline clone = pl.Clone() as Polyline;
using (clone)
{
clone.TransformBy(mt);
Extents3d ext = clone.GeometricExtents;
pointLL = (new Point3d(ext.MinPoint.X, ext.MinPoint.Y, 0.0))
.TransformBy(mt.Inverse());
pointRL = (new Point3d(ext.MaxPoint.X, ext.MinPoint.Y, 0.0))
.TransformBy(mt.Inverse());
pointRU = (new Point3d(ext.MaxPoint.X, ext.MaxPoint.Y, 0.0))
.TransformBy(mt.Inverse());
pointLU = (new Point3d(ext.MinPoint.X, ext.MaxPoint.Y, 0.0))
.TransformBy(mt.Inverse());
}
}

private static ObjectId OpenViewport(
Document dwg, string layoutName, string layerName, 
double twistAngle, double pScale, Point3d targetCenter)
{
Viewport vport = null;

using (Transaction tran = 
dwg.Database.TransactionManager.StartTransaction())
{
BlockTableRecord layoutBlock = 
GetLayoutBlock(tran, dwg.Database, layoutName);

double width;
double height;
Point3d centre;
GetLayoutSize(
dwg, layoutName, out centre, out width, out height);

vport = new Viewport();

vport.CenterPoint = new Point3d(centre.X, centre.Y, 0.0);
vport.Width = width;
vport.Height = height;
vport.Layer = layerName;

layoutBlock.UpgradeOpen();
layoutBlock.AppendEntity(vport);
tran.AddNewlyCreatedDBObject(vport, true);

vport.On = true; ;
vport.ViewDirection = Vector3d.ZAxis;
vport.ViewTarget = targetCenter;
vport.ViewCenter = Point2d.Origin;
vport.TwistAngle = Math.PI * 2 - twistAngle;
vport.CustomScale = pScale;
vport.Locked = true;

vport.UpdateDisplay();

tran.Commit();
}

return vport.ObjectId;
}

private static void ResizeViewport(
Document dwg, ObjectId vpId,
ViewportBoundaryInfo vportInfo,
double customScale)
{
Matrix3d mt = GetModelToPaperTransformMatrix(vpId);

Point3d pt1;
Point3d pt2;

//Get vport width
pt1 = vportInfo.PointLL.TransformBy(mt);
pt2 = vportInfo.PointRL.TransformBy(mt);

double vportW = Math.Abs(pt2.X - pt1.X); ;

//Get vport height
pt1 = vportInfo.PointLL.TransformBy(mt);
pt2 = vportInfo.PointLU.TransformBy(mt);

double vportH = Math.Abs(pt2.Y - pt1.Y);

using (Transaction tran = 
dwg.Database.TransactionManager.StartTransaction())
{
Viewport vport = (Viewport)
tran.GetObject(vpId, OpenMode.ForWrite);

Point3d center = vport.CenterPoint;

vport.Locked = false;
vport.Width = vportW;
vport.Height = vportH;
vport.CenterPoint = center;
vport.CustomScale = customScale;
vport.Locked = true;

vport.UpdateDisplay();

tran.Commit();
}
}

private static void GetLayoutSize(
Document dwg, string layoutName, 
out Point3d centre, out double width, out double height)
{
centre = new Point3d();
width = 0.0;
height = 0.0;

ObjectId layoutId = LayoutManager.Current.GetLayoutId(layoutName);

using (Transaction tran=dwg.TransactionManager.StartTransaction())
{
Layout layout = (Layout)
tran.GetObject(layoutId, OpenMode.ForRead);

double w = 
layout.PlotPaperSize.X - 
layout.PlotPaperMargins.MinPoint.X - 
layout.PlotPaperMargins.MaxPoint.X;
double h = layout.PlotPaperSize.Y - 
layout.PlotPaperMargins.MinPoint.Y - 
layout.PlotPaperMargins.MaxPoint.Y;

double s = layout.StdScale;
if (!layout.UseStandardScale)
{
s = layout.CustomPrintScale.Denominator / 
layout.CustomPrintScale.Numerator;
}

if (layout.PlotRotation==PlotRotation.Degrees090 || 
layout.PlotRotation==PlotRotation.Degrees270)
{
width = h;
height = w;
}
else
{
width = w;
height = h;
}

width = width * s;
height = height * s;

if (layout.PlotPaperUnits==PlotPaperUnit.Inches)
{
width = width / 25.4;
height = height / 25.4;
}
else if (layout.PlotPaperUnits==PlotPaperUnit.Pixels)
{
width = width / 25.4 * 72.0;
height = height / 25.4 * 72.0;
}

centre = new Point3d(width / 2.0, height / 2.0, 0.0);

tran.Commit();
}
}

private static BlockTableRecord GetLayoutBlock(
Transaction tran, Database db, string layoutName)
{
LayoutManager lmanager = LayoutManager.Current;
ObjectId layId = lmanager.GetLayoutId(layoutName);

Layout layout = tran.GetObject(layId, OpenMode.ForRead) as Layout;

return tran.GetObject(
layout.BlockTableRecordId, OpenMode.ForRead) 
as BlockTableRecord;
}

private static Matrix3d GetModelToPaperTransformMatrix(
ObjectId vportId)
{
Matrix3d mt;

using (Transaction tran = 
vportId.Database.TransactionManager.StartTransaction())
{
Viewport vp = (Viewport)
tran.GetObject(vportId, OpenMode.ForRead);
mt = ModelToPaper(vp);
tran.Commit();
}

return mt;
}

private static void TransformPointsFromModelToPaper(
ObjectId vpId, 
Point3dCollection modelPoints1, 
Point3dCollection modelPoints2,
out Point3dCollection paperPoints1, 
out Point3dCollection paperPoints2)
{
paperPoints1 = new Point3dCollection();
paperPoints2 = new Point3dCollection();
Matrix3d mt;
using (Transaction tran =
vpId.Database.TransactionManager.StartTransaction())
{
Viewport vp=(Viewport)tran.GetObject(vpId,OpenMode.ForRead);
mt = ModelToPaper(vp);
tran.Commit();
}

foreach (Point3d p in modelPoints1)
{
paperPoints1.Add(p.TransformBy(mt));
}

foreach (Point3d p in modelPoints2)
{
paperPoints2.Add(p.TransformBy(mt));
}
}

#endregion
}
}

As the code shows, the Viewport will be opened at the centre of the given layout.

Finally with all the code ready to use, here is the CommandClass MyCadCommands:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assemblyCommandClass(typeof(OpenViewportFromModelSpace.MyCadCommands))]

namespace OpenViewportFromModelSpace
{
    public class MyCadCommands
    {
        [CommandMethod("OpenVp")]
        public static void CreateRectangularViewport()
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            //Make sure start from ModelSpace
            LayoutManager.Current.CurrentLayout = "Model";
            
            //Pick model area to be shown in viewport
            ViewportBoundaryInfo vportInfo = GetUserInput(dwg);
           
            //Ask user for Layout name, exception test is omitted
            string layoutName = CadHelper.SelectLayout();

            //layer for the viewport, exception test is omitted
            string layerName = (string)
                Application.GetSystemVariable("CLAYER");

            //Viewport's custom scale, exception test is omitted
            double paperToModelScale = CadHelper.GetPaperToModelScale();

            if (!string.IsNullOrEmpty(layoutName) &&
                paperToModelScale != double.MinValue)
            {
                //Create viewport on specified layout
                try
                {
                    System.Windows.Forms.Cursor.Current =
                        System.Windows.Forms.Cursors.WaitCursor;

                    LayoutManager.Current.CurrentLayout = layoutName;

                    CadHelper.CreateRectangularViewport(
                        layoutName, layerName,
                        paperToModelScale, vportInfo);
                }
                catch (System.Exception ex)
                {
                    ed.WriteMessage(
                        "\nError: {0}.\n\n{1}", ex.Message, ex.StackTrace);
                }
                finally
                {
                    System.Windows.Forms.Cursor.Current =
                        System.Windows.Forms.Cursors.Default;
                }
            }
            else
            {
                ed.WriteMessage("\n*Cancel*");
            }

            Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
        }

        [CommandMethod("MyPolygon")]
        public static void DrawVisualPolygon()
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            GetUserInput(dwg);
        }

        private static ViewportBoundaryInfo GetUserInput(Document dwg)
        {
            ViewportBoundaryInfo vpInfo = null;
            using (OpenViewportVisualHelper helper =
                new OpenViewportVisualHelper(dwg))
            {
                if (helper.PickEnclosingPolygon())
                {
                    vpInfo = helper.ViewportBoundaryInfo;
                }
            }

            return vpInfo;
        }
    }
}

Watch this video clip to see how the code works.

In order to make the code shown here more user friendly in real AutoCAD operation, I could give user more options, such as after showing the Viewport boundary rectangle, I could let user drag/rotate the rectangle. so that the Viewport to be created would better fit the drawing content to be shown.

Download source code here.

Blog Archive

Labels

.NET Programming 2D Drafting 3D design 3D Modeling 3D models 3D Printing 3D rendering 3D scanning 3D Sketch Inventor 3D visualization 3D Web App 3ds Max ACC Add-in Development Additive Manufacturing AEC Technology affordable Autodesk tools AI Design AI in CAD AI in Manufacturing AI Tools AR architectural design architectural modeling architectural visualization Architecture architecture design Architecture Firm Architecture Productivity architecture software Artificial Intelligence As-Built Model augmented reality AutoCAD AutoCAD advice AutoCAD API AutoCAD Basics AutoCAD Beginners AutoCAD Civil 3D AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD tools AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk 2026 Autodesk 3ds Max Autodesk AI Autodesk AI Tools Autodesk Alias Autodesk AutoCAD Autodesk BIM Autodesk BIM 360 Autodesk Certification Autodesk Cloud Autodesk Construction Cloud Autodesk Docs Autodesk Dynamo Autodesk features Autodesk Forge Autodesk FormIt Autodesk Fusion 360 Autodesk InfraWorks Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk License Autodesk Navisworks Autodesk news Autodesk plugins Autodesk Recap Autodesk Revit Autodesk Software Autodesk Takeoff Autodesk Tips Autodesk training Autodesk update Autodesk Upgrade Autodesk Vault Autodesk Video Autodesk Viewer Automation Automation Tutorial automotive design automotive visualization Backup Basic Commands Basics Beginner Beginner Tips Big Data BIM BIM 360 BIM collaboration BIM Compliance BIM Coordination BIM Data BIM for Infrastructure BIM Implementation BIM Management BIM software BIM Standards BIM technology BIM tools BIM Trends BIM workflow Block Editor Building Maintenance Building Systems Building Technology ByLayer CAD CAD API CAD Automation CAD commands CAD comparison CAD Customization CAD Data Management CAD Design CAD Evolution CAD File Size Reduction CAD Integration CAD line thickness CAD management CAD modeling CAD Optimization CAD plugins CAD Productivity CAD Security CAD Skills CAD software CAD software 2026 CAD software training CAD technology CAD Tips CAD Tools CAD workflow car design software Case Study CGI design Civil 3D civil engineering Clash Detection Class-A surfacing clean CAD file cleaning command client engagement Cloud CAD Cloud Collaboration Cloud design platform Cloud Engineering Cloud Management Cloud Storage Cloud-First collaboration command abbreviations Complex Renovation concept car conceptual workflow construction Construction Analytics Construction BIM Construction Cloud Construction Planning Construction Technology contractor tools Contractor Workflow Contraints Cost Effective Design cost estimation Create resizable blocks CTB STB Cybersecurity Data Backup data management Data Protection Data Reference Data Security Data Shortcut Design Automation Design Collaboration Design Comparison Design Coordination design review Design Rules design software design software tips Design Technology Design Tools Design Workflow design-to-construction Designer Tools Digital Construction Digital Construction Technology Digital Design Digital engineering digital fabrication digital marketing digital takeoff Digital Twin Digital Twins digital workflow Drafting Standards Drawing Automation Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows Dynamo automation early stage design eco design editing commands Energy Analysis energy efficiency Engineering Engineering Automation engineering data Engineering Design Engineering Innovation Engineering Productivity engineering software Engineering Technology engineering tools Engineering Tools 2025 Engineering Workflow Excel Export Workflow Express Tools External Reference Facility Management Fast Structural Design Field Documentation File Optimization File Recovery Forge Development Forge Viewer FreeCAD Fusion 360 Fusion 360 API Fusion 360 tutorial Future Skills Generative Design Geospatial Data GIS Global design teams green building Green Technology Grips Handoff heavy CAD file Heavy CAD Files HVAC Design Tools HVAC Engineering Hydraulic Modeling iLogic Import Workflow Industry 4.0 infrastructure design Infrastructure Monitoring Infrastructure Planning Infrastructure Technology InfraWorks Insight interactive presentation Interior Design Inventor Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips IoT ISO 19650 Keyboard Shortcuts Large Projects Laser Scan Learn AutoCAD Licensing Linked Models Machine Learning in CAD maintenance command Management Manufacturing Innovation Mapping Technology marketing visuals Mechanical Design Mechanical Engineering MEP Modeling Mesh-to-BIM Metal Structure Model Optimization Multi-User Environment Navisworks Best Practices Net Zero Design ObjectARX .NET API Open Source CAD Organization OVERKILL OVERKILL AutoCAD Palette parametric design parametric family PDF PDM system PlanGrid Plot Style AutoCAD Plugin Tutorial point cloud Post Construction Practice Drawing preconstruction workflow Predictive Maintenance Printing Quality Product Design Product Development product lifecycle product rendering Productivity productivity tools professional printing Professional Tips project management Project Management Tools PTC Creo PURGE PURGE AutoCAD realistic rendering ReCap reduce CAD file size rendering engine rendering software Renovation Project Renovation Workflow Resizable Block Revit Revit add-ins Revit API Revit automation Revit Best Practices Revit integration Revit MEP Revit Performance Revit Phasing Revit Scripting Revit skills Revit Tips Revit tutorial Revit Workflow Ribbon ROI Scale Autodesk screen Secure Collaboration Sensor Data shortcut keys Shortcuts Siemens NX Simulation Sketch Small Firms Smart Architecture Smart Block Smart Building Design Smart City Smart Factory Smart Infrastructur Software Compliance Software Management Software Update SolidWorks Startup Design Steel Structure Design Structural Optimization Subscription Value sustainability sustainable design Sustainable Manufacturing team training guide Technical Drawing time saving tools toolbar Troubleshooting Tutorial urban planning User Interface (UI) Vault Best Practices Vault Lifecycle Vault Mistakes vehicle modeling virtual reality visualization workflow VR VR Tools VRED Water Infrastructure Water Management What’s New in Autodesk Workbook workflow Workflow Automation Workflow Optimization Worksets Worksharing Workspace XLS Xref