在AutoCAD中多视口展示多张图纸(AutoCAD2018及以上版本可用)

        书接上回,绘制了页面布局wpf窗体。没错,用户自定义的布局,就是用来在Autocad中展示图纸的。

        真实场景中,用户存在多张图纸,假设是平面图、剖面图、三维图。用户需要在AutoCAD中修改某些图纸的细节,使其更符合真实情况。但是修改图纸需要其它图纸做参考,以防出错。因此,同一个窗口,显示多张图纸,方便用户校对数据、修改的需求就出现了。

先上效果吧:

三视图

一开始想用最简单的方案,直接调用Autocad的命令,把多张图纸的tab窗体显示出来,让用户自己修改。发现这个不友好,一是用户得处理开始窗体。开始界面一直显示,没法拿到它的句柄,把它最小化,又不能侵入性地把开始界面藏起来。二是这个命令只能平分,遇到个性化需求就无法满足了。给大家看看这个方案出来的效果:

        查找资料,最后决定使用视口来解决这个问题。但是视口也有局限性,视口只能对一张图纸进行多视口操作,咱们多张图纸就显得力不从心了。于是就想了个能实现需求的办法,把多张图纸合并成一张,然后开3个视口,每个视口居中显示不同的范围,看起来就像在操作多张图纸了。

        但是也有一个局限,那就是其实三个视口显示的都是一张被合并的图纸,用户缩小或者拖拽,就找不到原先的视口了。于是我们加了个定位的按钮,记录下每个视口在整张图纸中的居中范围,用户找不到修改的图纸了,就可以点击这个按钮进行复原操作。

        还有最后一个问题,用户修改的图纸,怎么分成3张存回到对应的图纸上去?因为我们修改的是同一张图纸,虽然我们记录了原图在被合并后图纸中的范围,但是用户可能在任意范围添加图形元素。我们可以加个按钮,让用户在保存的时候框选区域,选择对应的图纸类型(屏幕、剖面、三维)就可以了。

        解决方案定了,剩下就是写代码的事情了。我们要做的事情如下:

  1. 把3张dwg图纸合并成1张dwg,并记录下原始图纸在合并后图纸的范围,用于后期做视口居中操作。注意,由于是多图操作,记得CommandFlags.Session设置上。本文简化使用了块参照。需要注意的是,块插入的时候插入点需要计算一下,防止图纸重叠。
  2. 按照比例创建想要的视口,并在不同视口对图纸进行不同范围的居中操作。

事情定了,就看看是不是已经有代码实现了这些操作。拼一拼,改一改,看能不能快速实现这个需求。查找了编程宝典《AutoCAD VBA&VB.NET开发基础与实例教程》有关于视口的代码,但是不能完全满足需求,代码最终是靠命令实现的视口创建。也许在当时那个版本,还没有C#的接口开放吧。最后参考了AutoCAD的官方blog,这篇Create four split modelspace viewports and set different orthographic views

拿着代码改了改,很快就实现了需求。

上源码

 [CommandMethod("STVP", CommandFlags.Session)]
 public void SpliterThreeViewPort()
 {
     string templateFile = @"acad.dwt";
     //把三张图纸变成块参照放到一张图纸中
     //todo:替换成你自己的图纸路径
     string sectionalView = "../剖面图.dwg";
     string threeDView = "../三维图.dwg";
     string planView = "../平面图.dwg";

     List<string> drawingNames = new List<string> { planView, sectionalView, threeDView };
     Dictionary<string, ObjectId> dicBlocks = new Dictionary<string, ObjectId>();

     //新建一张图纸
    //todo:替换成你自己的图纸路径
     string combinedPath = @"../combined.dwg";
     Document tempDoc = Application.DocumentManager.Add(templateFile);
     Application.DocumentManager.MdiActiveDocument = tempDoc;
     tempDoc.Database.SaveAs(combinedPath, DwgVersion.Current);

     Document combinDoc = Application.DocumentManager.Open(combinedPath);
     Application.DocumentManager.MdiActiveDocument = combinDoc;

     string blockName = string.Empty;
     List<string> blockNames = new List<string>();
     for (int i = 0; i < drawingNames.Count; i++)
     {
         blockName = Path.GetFileNameWithoutExtension(drawingNames[i]);
         ImportDrawingAsBlkstoCurDoc(combinDoc, drawingNames[i], combinedPath);
         blockNames.Add(blockName);
     }

     Dictionary<string, Extents3d> dicBlockExtents = InsertBlocks(combinDoc, blockNames);
     Application.DocumentManager.MdiActiveDocument = combinDoc;
     //打开图纸,分成3个视口,并且按照块参照的名称分别居中每个视口的图纸    
     SplitAndSetViewModelViewports(combinDoc, dicBlockExtents.Values.ToList());
 }
 public Dictionary<string, Extents3d> InsertBlocks(Document doc, List<string> blockNames)
 {
     // 记录已插入块参照的外包围框
     Dictionary<string, Extents3d> dicBlockExtents = new Dictionary<string, Extents3d>();

     Database db = doc.Database;
     // 获取要插入的块名称列表

     using (DocumentLock docLock = doc.LockDocument())
     {
         db.UpdateExt(true);
         using (Transaction tr = db.TransactionManager.StartTransaction())
         {
             BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
             BlockTableRecord modelSpace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;


             Point3d insertPosition = Point3d.Origin;

             foreach (string blockName in blockNames)
             {
                 // 获取块定义
                 ObjectId blockId = bt[blockName];
                 if (blockId.IsNull)
                 {
                     continue;
                 }

                 // 创建块参照
                 using (BlockReference blockRef = new BlockReference(insertPosition, blockId))
                 {
                     // 计算块的外包围框
                     blockRef.TransformBy(Matrix3d.Scaling(1, insertPosition));
                     Extents3d blockExtents = blockRef.GeometricExtents;

                     // 将块参照插入模型空间
                     modelSpace.AppendEntity(blockRef);
                     tr.AddNewlyCreatedDBObject(blockRef, true);

                     // 更新已插入块参照的外包围框列表
                     dicBlockExtents.Add(blockName, blockExtents);
                     insertPosition = blockExtents.MaxPoint;
                 }
             }
             tr.Commit();
         }
     }
     return dicBlockExtents;
 }
public static void SplitAndSetViewModelViewports(Document doc, Dictionary<string, Extents3d> dicExtents)
{
    using (DocumentLock docLock = doc.LockDocument())
    {
        Database db = doc.Database;

        db.UpdateExt(true);
        Extents3d dbExtent = new Extents3d(db.Extmin, db.Extmax);

        using (Transaction tr = db.TransactionManager.StartTransaction())
        {
            ViewportTable vt = tr.GetObject(
                db.ViewportTableId, OpenMode.ForWrite)
                as ViewportTable;

            ViewportTableRecord vtr1 = tr.GetObject(
                doc.Editor.ActiveViewportId,
                OpenMode.ForWrite) as ViewportTableRecord;

            Point2d ll = vtr1.LowerLeftCorner;
            Point2d ur = vtr1.UpperRightCorner;

            List<string> extentsKeys = dicExtents.Keys.ToList();

            vtr1.LowerLeftCorner = ll;
            vtr1.UpperRightCorner = new Point2d(
                ll.X + (ur.X - ll.X) * 0.5,
                ll.Y + (ur.Y - ll.Y));
            vtr1.SetViewDirection(OrthographicView.TopView);
            ZoomExtents(vtr1, dicExtents[extentsKeys[0]]);

            ViewportTableRecord vtr2 =
            CreateVTR(vt, vtr1,
            new Point2d(ll.X + (ur.X - ll.X) * 0.5, ll.Y + (ur.Y - ll.Y) * 0.5),
            new Point2d(ll.X + (ur.X - ll.X), ll.Y + (ur.Y - ll.Y)),
            dicExtents[extentsKeys[1]], OrthographicView.TopView);
            vt.Add(vtr2);
            tr.AddNewlyCreatedDBObject(vtr2, true);

            ViewportTableRecord vtr3 =

                CreateVTR(vt, vtr1,
                    new Point2d(ll.X + (ur.X - ll.X) * 0.5, ll.Y),
                    new Point2d(ll.X + (ur.X - ll.X), ll.Y + (ur.Y - ll.Y) * 0.5),
                dicExtents[extentsKeys[2]], OrthographicView.TopView);
            vt.Add(vtr3);
            tr.AddNewlyCreatedDBObject(vtr3, true);

            // Update the display with new tiled viewports 
            doc.Editor.UpdateTiledViewportsFromDatabase();

            // Commit the changes  
            tr.Commit();
        }
    }

}

/// <summary>
/// 将图纸作为一个块整个插入导数据库
/// </summary>
/// <param name="destDb"></param>
/// <param name="FileName">图纸的完整路径,dxf或者dwg都可以</param>
/// <returns></returns>
/// 
public static void ImportDrawingAsBlkstoCurDoc(Document destDoc, string strFileName, string savePath)
{
    using (DocumentLock docLock = destDoc.LockDocument())
    {
        using (Database db = new Database(false, true))
        {
            // Read the DWG into our side database
            db.ReadDwgFile(strFileName, FileShare.Read, true, "");
            // Create a list of block identifiers (will only contain one entry, the modelspace ObjectId)
            ObjectIdCollection ids = new ObjectIdCollection();
            // Start a transaction on the source database

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // Open the block table
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

                // Add modelspace to list of blocks to import
                ids.Add(bt[BlockTableRecord.ModelSpace]);

                // Committing is cheaper than aborting
                tr.Commit();
            }
            // Copy our modelspace block from the source to

            // destination database

            // (will also copy required, referenced objects)

            IdMapping im = new IdMapping();

            db.WblockCloneObjects(
              ids,
              destDoc.Database.BlockTableId,
              im,
              DuplicateRecordCloning.MangleName,
              false
            );

            using (Transaction tr2 = destDoc.Database.TransactionManager.StartTransaction())
            {
                // Work through the results of the WblockClone
                foreach (IdPair ip in im)
                {
                    // Open each new destination object, checking for

                    // BlockTableRecords

                    BlockTableRecord btr = tr2.GetObject(ip.Value, OpenMode.ForRead) as BlockTableRecord;
                    if (btr != null)
                    {
                        // If the name starts with the modelspace string

                        if (btr.Name.StartsWith(BlockTableRecord.ModelSpace, StringComparison.InvariantCultureIgnoreCase))
                        {
                            // Get write access to it and change the name

                            // to that of the source drawing

                            btr.UpgradeOpen();
                            btr.Name = Path.GetFileNameWithoutExtension(strFileName);
                        }
                    }
                }
                db.UpdateExt(true);
                // We need to commit, as we've made changes
                tr2.Commit();
            }
            db.SaveAs(savePath, DwgVersion.Current);
        }
    }
}

public static ViewportTableRecord CreateVTR(
   ViewportTable vt, ViewportTableRecord refVTR,
   Point2d ll, Point2d ur, Extents3d dbExtent,
   OrthographicView ov)
{
    ViewportTableRecord newVTR = new ViewportTableRecord();

    newVTR.LowerLeftCorner = ll;
    newVTR.UpperRightCorner = ur;
    newVTR.Name = "*Active";

    newVTR.ViewDirection = refVTR.ViewDirection;
    newVTR.ViewTwist = refVTR.ViewTwist;
    newVTR.Target = refVTR.Target;
    newVTR.BackClipEnabled = refVTR.BackClipEnabled;
    newVTR.BackClipDistance = refVTR.BackClipDistance;
    newVTR.FrontClipEnabled = refVTR.FrontClipEnabled;
    newVTR.FrontClipDistance = refVTR.FrontClipDistance;
    newVTR.Elevation = refVTR.Elevation;
    newVTR.SetViewDirection(ov);

    ZoomExtents(newVTR, dbExtent);

    return newVTR;
}

public static void ZoomExtents
    (ViewportTableRecord vtr, Extents3d dbExtent)
{
    //get the screen aspect ratio to  
    // calculate the height and width 
    double scrRatio = (vtr.Width / vtr.Height);

    //prepare Matrix for DCS to WCS transformation 
    Matrix3d matWCS2DCS
        = Matrix3d.PlaneToWorld(vtr.ViewDirection);

    //for DCS target point is the origin 
    matWCS2DCS = Matrix3d.Displacement
        (vtr.Target - Point3d.Origin) * matWCS2DCS;

    //WCS Xaxis is twisted by twist angle 
    matWCS2DCS = Matrix3d.Rotation(-vtr.ViewTwist,
                                    vtr.ViewDirection,
                                    vtr.Target
                                ) * matWCS2DCS;

    matWCS2DCS = matWCS2DCS.Inverse();

    //tranform the extents to the DCS  
    // defined by the viewdir 
    dbExtent.TransformBy(matWCS2DCS);

    //width of the extents in current view 
    double width
         = (dbExtent.MaxPoint.X - dbExtent.MinPoint.X);

    //height of the extents in current view 
    double height
         = (dbExtent.MaxPoint.Y - dbExtent.MinPoint.Y);

    //get the view center point 
    Point2d center = new Point2d(
         (dbExtent.MaxPoint.X + dbExtent.MinPoint.X) * 0.5,
         (dbExtent.MaxPoint.Y + dbExtent.MinPoint.Y) * 0.5);

    //check if the width' in current window 
    //if not then get the new height as per the  
    // viewports aspect ratio 
    if (width > (height * scrRatio))
        height = width / scrRatio;

    vtr.Height = height;
    vtr.Width = height * scrRatio;
    vtr.CenterPoint = center;
}

【转载声明】转载本博客的文章请注明原始出处和作者

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值