明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 448|回复: 9

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

[复制链接]
发表于 2024-3-19 19:46 | 显示全部楼层 |阅读模式

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

先上效果吧:

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

   查找资料,最后决定使用视口来解决这个问题。但是视口也有局限性,视口只能对一张图纸进行多视口操作,咱们多张图纸就显得力不从心了。于是就想了个能实现需求的办法,把多张图纸合并成一张,然后开3个视口,每个视口居中显示不同的范围,看起来就像在操作多张图纸了。
        但是也有一个局限,那就是其实三个视口显示的都是一张被合并的图纸,用户缩小或者拖拽,就找不到原先的视口了。于是我们加了个定位的按钮,记录下每个视口在整张图纸中的居中范围,用户找不到修改的图纸了,就可以点击这个按钮进行复原操作。
        还有最后一个问题,用户修改的图纸,怎么分成3张存回到对应的图纸上去?因为我们修改的是同一张图纸,虽然我们记录了原图在被合并后图纸中的范围,但是用户可能在任意范围添加图形元素。我们可以加个按钮,让用户在保存的时候框选区域,选择对应的图纸类型(屏幕、剖面、三维)就可以了。
        解决方案定了,剩下就是写代码的事情了。我们要做的事情如下:
  • 把3张dwg图纸合并成1张dwg,并记录下原始图纸在合并后图纸的范围,用于后期做视口居中操作。注意,由于是多图操作,记得CommandFlags.Session设置上。本文简化使用了块参照。需要注意的是,块插入的时候插入点需要计算一下,防止图纸重叠。
  • 按照比例创建想要的视口,并在不同视口对图纸进行不同范围的居中操作。


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


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

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

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

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

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

  20.      string blockName = string.Empty;
  21.      List<string> blockNames = new List<string>();
  22.      for (int i = 0; i < drawingNames.Count; i++)
  23.      {
  24.          blockName = Path.GetFileNameWithoutExtension(drawingNames);
  25.          ImportDrawingAsBlkstoCurDoc(combinDoc, drawingNames, combinedPath);
  26.          blockNames.Add(blockName);
  27.      }

  28.      Dictionary<string, Extents3d> dicBlockExtents = InsertBlocks(combinDoc, blockNames);
  29.      Application.DocumentManager.MdiActiveDocument = combinDoc;
  30.      //打开图纸,分成3个视口,并且按照块参照的名称分别居中每个视口的图纸   
  31.      SplitAndSetViewModelViewports(combinDoc, dicBlockExtents.Values.ToList());
  32. }


  1. public Dictionary<string, Extents3d> InsertBlocks(Document doc, List<string> blockNames)
  2. {
  3.      // 记录已插入块参照的外包围框
  4.      Dictionary<string, Extents3d> dicBlockExtents = new Dictionary<string, Extents3d>();

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

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


  14.              Point3d insertPosition = Point3d.Origin;

  15.              foreach (string blockName in blockNames)
  16.              {
  17.                  // 获取块定义
  18.                  ObjectId blockId = bt[blockName];
  19.                  if (blockId.IsNull)
  20.                  {
  21.                      continue;
  22.                  }

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

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

  32.                      // 更新已插入块参照的外包围框列表
  33.                      dicBlockExtents.Add(blockName, blockExtents);
  34.                      insertPosition = blockExtents.MaxPoint;
  35.                  }
  36.              }
  37.              tr.Commit();
  38.          }
  39.      }
  40.      return dicBlockExtents;
  41. }


  1. public static void SplitAndSetViewModelViewports(Document doc, Dictionary<string, Extents3d> dicExtents)
  2. {
  3.     using (DocumentLock docLock = doc.LockDocument())
  4.     {
  5.         Database db = doc.Database;

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

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

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

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

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

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

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

  32.             ViewportTableRecord vtr3 =

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

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

  41.             // Commit the changes  
  42.             tr.Commit();
  43.         }
  44.     }

  45. }

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

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

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

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

  74.             // destination database

  75.             // (will also copy required, referenced objects)

  76.             IdMapping im = new IdMapping();

  77.             db.WblockCloneObjects(
  78.               ids,
  79.               destDoc.Database.BlockTableId,
  80.               im,
  81.               DuplicateRecordCloning.MangleName,
  82.               false
  83.             );

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

  90.                     // BlockTableRecords

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

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

  98.                             // to that of the source drawing

  99.                             btr.UpgradeOpen();
  100.                             btr.Name = Path.GetFileNameWithoutExtension(strFileName);
  101.                         }
  102.                     }
  103.                 }
  104.                 db.UpdateExt(true);
  105.                 // We need to commit, as we've made changes
  106.                 tr2.Commit();
  107.             }
  108.             db.SaveAs(savePath, DwgVersion.Current);
  109.         }
  110.     }
  111. }

  112. public static ViewportTableRecord CreateVTR(
  113.    ViewportTable vt, ViewportTableRecord refVTR,
  114.    Point2d ll, Point2d ur, Extents3d dbExtent,
  115.    OrthographicView ov)
  116. {
  117.     ViewportTableRecord newVTR = new ViewportTableRecord();

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

  121.     newVTR.ViewDirection = refVTR.ViewDirection;
  122.     newVTR.ViewTwist = refVTR.ViewTwist;
  123.     newVTR.Target = refVTR.Target;
  124.     newVTR.BackClipEnabled = refVTR.BackClipEnabled;
  125.     newVTR.BackClipDistance = refVTR.BackClipDistance;
  126.     newVTR.FrontClipEnabled = refVTR.FrontClipEnabled;
  127.     newVTR.FrontClipDistance = refVTR.FrontClipDistance;
  128.     newVTR.Elevation = refVTR.Elevation;
  129.     newVTR.SetViewDirection(ov);

  130.     ZoomExtents(newVTR, dbExtent);

  131.     return newVTR;
  132. }

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

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

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

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

  150.     matWCS2DCS = matWCS2DCS.Inverse();

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

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

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

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

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

  169.     vtr.Height = height;
  170.     vtr.Width = height * scrRatio;
  171.     vtr.CenterPoint = center;
  172. }









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 1明经币 +1 金钱 +10 收起 理由
tigcat + 1 + 10 很给力!

查看全部评分

发表于 2024-3-19 20:47 | 显示全部楼层
早说要VTR,为什么不早说...阿惊啥东西没敲过(嘻)
回复 支持 1 反对 0

使用道具 举报

发表于 2024-3-19 22:34 | 显示全部楼层
你有种再说一遍 发表于 2024-3-19 20:47
早说要VTR,为什么不早说...阿惊啥东西没敲过(嘻)

大家一起喊:“惊神”(破音)
发表于 2024-3-20 07:56 | 显示全部楼层
感谢分享感谢分享感谢分享
发表于 2024-3-20 12:03 | 显示全部楼层
表示没看懂
发表于 2024-3-20 12:55 | 显示全部楼层
只能膜拜大佬!
发表于 2024-3-21 16:12 | 显示全部楼层
我们实际工作中,通常对于这种多个文件互相参考的时候,基本还是直接tile显示全部打开的文档。如果打开的文件数量较多,确实想要一下就找到自己需要查看的那个文档是不太可能的。

所以在机器能带动的情况下,外部参照其他文件是最合适的做法。
多个视口的管理,视口的恢复等,内置的命令在交互上效率是比较低的,所以近年来的版本,在界面上加了 ViewCube, ViewControl等工具。

为了应对工作中的这类需求,之前也写了几个Vports的工具,例如双视口位置同步观察,多视口仿鹰眼,VportConfig快速切换等。
发表于 2024-3-21 16:24 | 显示全部楼层
我都是在图纸上加链接,总装、部件、零件一级级的通过图纸编号链接起来。

如果有几张图,相互参照,可以直接插入一张总图来看。

点评

会用hyperlink的人太少了。  发表于 2024-3-21 20:36
发表于 2024-3-22 09:34 | 显示全部楼层
e2002 发表于 2024-3-21 16:12
我们实际工作中,通常对于这种多个文件互相参考的时候,基本还是直接tile显示全部打开的文档。如果打开的文 ...

个人以为还是外部参照比较现实,这里说的技术作为技术文档和实现还是很牛掰的。
但是个人觉得,实际应用时,简单图形还行,如果复杂图形,感觉会更难操作。

外部参照完全可以实现本文所要达到的功能。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-5-3 12:46 , Processed in 0.464997 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表