真实场景中,用户存在多张图纸,假设是平面图、剖面图、三维图。用户需要在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
拿着代码改了改,很快就实现了需求。
上源码
- [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);
- 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[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;
- }
|