在AutoCAD中多视口展示多张图纸(AutoCAD2018及以上版本可用)
真实场景中,用户存在多张图纸,假设是平面图、剖面图、三维图。用户需要在AutoCAD中修改某些图纸的细节,使其更符合真实情况。但是修改图纸需要其它图纸做参考,以防出错。因此,同一个窗口,显示多张图纸,方便用户校对数据、修改的需求就出现了。先上效果吧:一开始想用最简单的方案,直接调用Autocad的命令,把多张图纸的tab窗体显示出来,让用户自己修改。发现这个不友好,一是用户得处理开始窗体。开始界面一直显示,没法拿到它的句柄,把它最小化,又不能侵入性地把开始界面藏起来。二是这个命令只能平分,遇到个性化需求就无法满足了。给大家看看这个方案出来的效果: 查找资料,最后决定使用视口来解决这个问题。但是视口也有局限性,视口只能对一张图纸进行多视口操作,咱们多张图纸就显得力不从心了。于是就想了个能实现需求的办法,把多张图纸合并成一张,然后开3个视口,每个视口居中显示不同的范围,看起来就像在操作多张图纸了。但是也有一个局限,那就是其实三个视口显示的都是一张被合并的图纸,用户缩小或者拖拽,就找不到原先的视口了。于是我们加了个定位的按钮,记录下每个视口在整张图纸中的居中范围,用户找不到修改的图纸了,就可以点击这个按钮进行复原操作。
还有最后一个问题,用户修改的图纸,怎么分成3张存回到对应的图纸上去?因为我们修改的是同一张图纸,虽然我们记录了原图在被合并后图纸中的范围,但是用户可能在任意范围添加图形元素。我们可以加个按钮,让用户在保存的时候框选区域,选择对应的图纸类型(屏幕、剖面、三维)就可以了。
解决方案定了,剩下就是写代码的事情了。我们要做的事情如下:
[*]把3张dwg图纸合并成1张dwg,并记录下原始图纸在合并后图纸的范围,用于后期做视口居中操作。注意,由于是多图操作,记得CommandFlags.Session设置上。本文简化使用了块参照。需要注意的是,块插入的时候插入点需要计算一下,防止图纸重叠。
[*]按照比例创建想要的视口,并在不同视口对图纸进行不同范围的居中操作。
事情定了,就看看是不是已经有代码实现了这些操作。拼一拼,改一改,看能不能快速实现这个需求。查找了编程宝典《AutoCAD VBA&VB.NET开发基础与实例教程》有关于视口的代码,但是不能完全满足需求,代码最终是靠命令实现的视口创建。也许在当时那个版本,还没有C#的接口开放吧。最后参考了AutoCAD的官方blog,这篇
https://adndevblog.typepad.com/autocad/2015/08/create-four-split-modelspace-viewports-and-set-different-orthographic-views.html
拿着代码改了改,很快就实现了需求。
上源码
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);
ImportDrawingAsBlkstoCurDoc(combinDoc, drawingNames, 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, OpenMode.ForWrite) as BlockTableRecord;
Point3d insertPosition = Point3d.Origin;
foreach (string blockName in blockNames)
{
// 获取块定义
ObjectId blockId = bt;
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]);
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], 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], 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);
// 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;
}
早说要VTR,为什么不早说...阿惊啥东西没敲过(嘻) 你有种再说一遍 发表于 2024-3-19 20:47
早说要VTR,为什么不早说...阿惊啥东西没敲过(嘻)
大家一起喊:“惊神”(破音) 感谢分享感谢分享感谢分享 表示没看懂 只能膜拜大佬! 我们实际工作中,通常对于这种多个文件互相参考的时候,基本还是直接tile显示全部打开的文档。如果打开的文件数量较多,确实想要一下就找到自己需要查看的那个文档是不太可能的。
所以在机器能带动的情况下,外部参照其他文件是最合适的做法。
多个视口的管理,视口的恢复等,内置的命令在交互上效率是比较低的,所以近年来的版本,在界面上加了 ViewCube, ViewControl等工具。
为了应对工作中的这类需求,之前也写了几个Vports的工具,例如双视口位置同步观察,多视口仿鹰眼,VportConfig快速切换等。
我都是在图纸上加链接,总装、部件、零件一级级的通过图纸编号链接起来。
如果有几张图,相互参照,可以直接插入一张总图来看。 e2002 发表于 2024-3-21 16:12
我们实际工作中,通常对于这种多个文件互相参考的时候,基本还是直接tile显示全部打开的文档。如果打开的文 ...
个人以为还是外部参照比较现实,这里说的技术作为技术文档和实现还是很牛掰的。
但是个人觉得,实际应用时,简单图形还行,如果复杂图形,感觉会更难操作。
外部参照完全可以实现本文所要达到的功能。
页:
[1]