In other case, given a point, or an entity, in ModelSpace, we may want to determine which viewport or viewports on a layout the point/entity can be seen.
Note: there is a post in Autodesk's user forum on this topic.
I began with code that collects Viewport information on a given layout. The information is used to determine which entities in ModelSpace are visible through each Viewport, and is collected in one single transaction. Here is the code:
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.DatabaseServices;
3 using Autodesk.AutoCAD.EditorInput;
4 using Autodesk.AutoCAD.Geometry;
5 using System;
6 using System.Collections.Generic;
7
8 namespace EntitiesInsideViewport
9 {
10 //Class to hold Viewport information, obtained
11 //in single Transaction
12 public class ViewportInfo
13 {
14 public ObjectId ViewportId { set; get; }
15 public ObjectId NonRectClipId { set; get; }
16 public Point3dCollection BoundaryInPaperSpace { set; get; }
17 public Point3dCollection BoundaryInModelSpace { set; get; }
18 }
19
20 public class CadHelper
21 {
22 //Get needed Viewport information
23 public static ViewportInfo[] SelectLockedViewportInfoOnLayout(
24 Document dwg, string layoutName)
25 {
26 List<ViewportInfo> lst = new List<ViewportInfo>();
27 TypedValue[] vals = new TypedValue[]{
28 new TypedValue((int)DxfCode.Start, "VIEWPORT"),
29 new TypedValue((int)DxfCode.LayoutName,layoutName)
30 };
31
32 PromptSelectionResult res =
33 dwg.Editor.SelectAll(new SelectionFilter(vals));
34 if (res.Status==PromptStatus.OK)
35 {
36 using (Transaction tran=
37 dwg.TransactionManager.StartTransaction())
38 {
39 foreach (ObjectId id in res.Value.GetObjectIds())
40 {
41 Viewport vport = (Viewport)tran.GetObject(
42 id, OpenMode.ForRead);
43 if (vport.Number!=1 && vport.Locked)
44 {
45 ViewportInfo vpInfo = new ViewportInfo();
46 vpInfo.ViewportId = id;
47 vpInfo.NonRectClipId = vport.NonRectClipEntityId;
48 if (!vport.NonRectClipEntityId.IsNull &&
49 vport.NonRectClipOn)
50 {
// //Polyline2d pl = (Polyline2d)tran.GetObject(
// // vport.NonRectClipEntityId, OpenMode.ForRead);
52 Polyline pl = (Polyline)tran.GetObject(
52 vport.NonRectClipEntityId, OpenMode.ForRead);
53 vpInfo.BoundaryInPaperSpace =
54 GetNonRectClipBoundary(pl, tran);
55 }
56 else
57 {
58 vpInfo.BoundaryInPaperSpace =
59 GetViewportBoundary(vport);
60 }
61
62 Matrix3d mt = PaperToModel(vport);
63 vpInfo.BoundaryInModelSpace =
64 TransformPaperSpacePointToModelSpace(
65 vpInfo.BoundaryInPaperSpace, mt);
66
67 lst.Add(vpInfo);
68 }
69 }
70
71 tran.Commit();
72 }
73 }
74
75 return lst.ToArray();
76 }
77
78 private static Point3dCollection GetViewportBoundary(Viewport vport)
79 {
80 Point3dCollection points = new Point3dCollection();
81
82 Extents3d ext = vport.GeometricExtents;
83 points.Add(new Point3d(ext.MinPoint.X, ext.MinPoint.Y, 0.0));
84 points.Add(new Point3d(ext.MinPoint.X, ext.MaxPoint.Y, 0.0));
85 points.Add(new Point3d(ext.MaxPoint.X, ext.MaxPoint.Y, 0.0));
86 points.Add(new Point3d(ext.MaxPoint.X, ext.MinPoint.Y, 0.0));
87
88 return points;
89 }
90
91 private static Point3dCollection GetNonRectClipBoundary(
92 Polyline polyline, Transaction tran)
93 {
94 Point3dCollection points = new Point3dCollection();
95
// foreach (ObjectId vxId in polyline)
// {
// Vertex2d vx = (Vertex2d)tran.GetObject(vxId, OpenMode.ForRead);
// points.Add(polyline.VertexPosition(vx));
// }
96 for (int i = 0; i < polyline.NumberOfvertices; i++)
97 {
98
99 points.Add(polyline.GetPoint3dAt(i);
100 }
101
102 return points;
103 }
104
105 private static Point3dCollection TransformPaperSpacePointToModelSpace(
106 Point3dCollection paperSpacePoints, Matrix3d mt)
107 {
108 Point3dCollection points = new Point3dCollection();
109
110 foreach (Point3d p in paperSpacePoints)
111 {
112 points.Add(p.TransformBy(mt));
113 }
114
115 return points;
116 }
117
118 #region
119 //**********************************************************************
120 //Create coordinate transform matrix
121 //between modelspace and paperspace viewport
122 //The code is borrowed from
123 //http://www.theswamp.org/index.php?topic=34590.msg398539#msg398539
124 //*********************************************************************
125 public static Matrix3d PaperToModel(Viewport vp)
126 {
127 Matrix3d mx = ModelToPaper(vp);
128 return mx.Inverse();
129 }
130
131 public static Matrix3d ModelToPaper(Viewport vp)
132 {
133 Vector3d vd = vp.ViewDirection;
134 Point3d vc = new Point3d(vp.ViewCenter.X, vp.ViewCenter.Y, 0);
135 Point3d vt = vp.ViewTarget;
136 Point3d cp = vp.CenterPoint;
137 double ta = -vp.TwistAngle;
138 double vh = vp.ViewHeight;
139 double height = vp.Height;
140 double width = vp.Width;
141 double scale = vh / height;
142 double lensLength = vp.LensLength;
143 Vector3d zaxis = vd.GetNormal();
144 Vector3d xaxis = Vector3d.ZAxis.CrossProduct(vd);
145 Vector3d yaxis;
146
147 if (!xaxis.IsZeroLength())
148 {
149 xaxis = xaxis.GetNormal();
150 yaxis = zaxis.CrossProduct(xaxis);
151 }
152 else if (zaxis.Z < 0)
153 {
154 xaxis = Vector3d.XAxis * -1;
155 yaxis = Vector3d.YAxis;
156 zaxis = Vector3d.ZAxis * -1;
157 }
158 else
159 {
160 xaxis = Vector3d.XAxis;
161 yaxis = Vector3d.YAxis;
162 zaxis = Vector3d.ZAxis;
163 }
164 Matrix3d pcsToDCS = Matrix3d.Displacement(Point3d.Origin - cp);
165 pcsToDCS = pcsToDCS * Matrix3d.Scaling(scale, cp);
166 Matrix3d dcsToWcs = Matrix3d.Displacement(vc - Point3d.Origin);
167 Matrix3d mxCoords = Matrix3d.AlignCoordinateSystem(
168 Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis,
169 Vector3d.ZAxis, Point3d.Origin,
170 xaxis, yaxis, zaxis);
171 dcsToWcs = mxCoords * dcsToWcs;
172 dcsToWcs = Matrix3d.Displacement(vt - Point3d.Origin) * dcsToWcs;
173 dcsToWcs = Matrix3d.Rotation(ta, zaxis, vt) * dcsToWcs;
174
175 Matrix3d perspectiveMx = Matrix3d.Identity;
176 if (vp.PerspectiveOn)
177 {
178 double vSize = vh;
179 double aspectRatio = width / height;
180 double adjustFactor = 1.0 / 42.0;
181 double adjstLenLgth = vSize * lensLength *
182 Math.Sqrt(1.0 + aspectRatio * aspectRatio) * adjustFactor;
183 double iDist = vd.Length;
184 double lensDist = iDist - adjstLenLgth;
185 double[] dataAry = new double[]
186 {
187 1,0,0,0,0,1,0,0,0,0,
188 (adjstLenLgth-lensDist)/adjstLenLgth,
189 lensDist*(iDist-adjstLenLgth)/adjstLenLgth,
190 0,0,-1.0/adjstLenLgth,iDist/adjstLenLgth
191 };
192
193 perspectiveMx = new Matrix3d(dataAry);
194 }
195
196 Matrix3d finalMx =
197 pcsToDCS.Inverse() * perspectiveMx * dcsToWcs.Inverse();
198
199 return finalMx;
200 }
201
202 #endregion
203 }
204 }
Now the following code does 2 things we want to do very often: finding out which entities in ModelSpace are visible in which Viewport; and determining a given entity in ModelSpace is visible in which Viewports:
1 using System.Collections.Generic;
2 using Autodesk.AutoCAD.ApplicationServices;
3 using Autodesk.AutoCAD.DatabaseServices;
4 using Autodesk.AutoCAD.EditorInput;
5 using Autodesk.AutoCAD.Geometry;
6 using Autodesk.AutoCAD.Runtime;
7
8 [assembly: CommandClass(typeof(EntitiesInsideViewport.MyCommands))]
9
10 namespace EntitiesInsideViewport
11 {
12 public class MyCommands
13 {
14 //Use viewport boundary as selecting window/polygon
15 //to find entities in modelspace visible in each viewport
16 [CommandMethod("VpSelect")]
17 public static void SelectByViewport()
18 {
19 Document dwg = Application.DocumentManager.MdiActiveDocument;
20 Editor ed = dwg.Editor;
21
22 //Save current layout name
23 string curLayout = LayoutManager.Current.CurrentLayout;
24
25 try
26 {
27 //Get viewport information on current layout
28 ViewportInfo[] vports = GetViewportInfoOnCurrentLayout();
29 if (vports == null) return;
30
31 //Switch to modelspace
32 LayoutManager.Current.CurrentLayout = "Model";
33
34 //Select entities in modelspace that are visible
35 foreach (ViewportInfo vInfo in vports)
36 {
37 ObjectId[] ents = SelectEntitisInModelSpaceByViewport(
38 dwg, vInfo.BoundaryInModelSpace);
39 ed.WriteMessage("\n{0} entit{1} found via Viewport \"{2}\"",
40 ents.Length,
41 ents.Length > 1 ? "ies" : "y",
42 vInfo.ViewportId.ToString());
43 }
44
45 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
46 }
47 catch (System.Exception ex)
48 {
49 ed.WriteMessage("\nCommand \"VpSelect\" failed:");
50 ed.WriteMessage("\n{0}\n{1}", ex.Message, ex.StackTrace);
51 }
52 finally
53 {
54 //Restore back to original layout
55 if (LayoutManager.Current.CurrentLayout!=curLayout)
56 {
57 LayoutManager.Current.CurrentLayout = curLayout;
58 }
59
60 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
61 }
62 }
63
64 //Determine a given entity in modelspace is visible in
65 //which viewports
66 [CommandMethod("GetViewports")]
67 public static void FindContainingViewport()
68 {
69 Document dwg = Application.DocumentManager.MdiActiveDocument;
70 Editor ed = dwg.Editor;
71
72 //Switch to modelspace
73 string curLayout = LayoutManager.Current.CurrentLayout;
74
75 try
76 {
77 //Get viewport information on current layout
78 ViewportInfo[] vports = GetViewportInfoOnCurrentLayout();
79 if (vports == null) return;
80
81 //Pick an entity in modelspace
82 LayoutManager.Current.CurrentLayout = "Model";
83 ObjectId entId = PickEntity(ed);
84 if (entId.IsNull)
85 {
86 ed.WriteMessage("\n*Cancel*");
87 }
88 else
89 {
90 //Find viewport in which the selected entity is visible
91 List<ObjectId> lst = new List<ObjectId>();
92 foreach (ViewportInfo vpInfo in vports)
93 {
94 if (IsEntityInsideViewportBoundary(
95 dwg, entId, vpInfo.BoundaryInModelSpace))
96 {
97 lst.Add(vpInfo.ViewportId);
98 ed.WriteMessage(
99 "\nSelected entity is visible in viewport \"{0}\"",
100 vpInfo.ViewportId.ToString());
101 }
102 }
103
104 if (lst.Count == 0)
105 ed.WriteMessage(
106 "\nSelected entity is not visible in all viewports");
107 else
108 ed.WriteMessage(
109 "\nSelected entity is visible in {0} viewport{1}.",
110 lst.Count, lst.Count > 1 ? "s" : "");
111 }
112
113 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
114 }
115 catch (System.Exception ex)
116 {
117 ed.WriteMessage("\nCommand \"GetViewports\" failed:");
118 ed.WriteMessage("\n{0}\n{1}", ex.Message, ex.StackTrace);
119 }
120 finally
121 {
122 //Restore back to original layout
123 if (LayoutManager.Current.CurrentLayout != curLayout)
124 {
125 LayoutManager.Current.CurrentLayout = curLayout;
126 }
127
128 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
129 }
130 }
131
132 private static ViewportInfo[] GetViewportInfoOnCurrentLayout()
133 {
134 string layoutName = LayoutManager.Current.CurrentLayout;
135 if (layoutName.ToUpper() == "MODEL")
136 {
137 Application.ShowAlertDialog("Please set a layout as active layout!");
138 return null;
139 }
140 else
141 {
142 Document dwg = Application.DocumentManager.MdiActiveDocument;
143 ViewportInfo[] vports =
144 CadHelper.SelectLockedViewportInfoOnLayout(dwg, layoutName);
145 if (vports.Length == 0)
146 {
147 Application.ShowAlertDialog(
148 "No locked viewport found on layout \"" + layoutName + "\".");
149 return null;
150 }
151 else
152 {
153 return vports;
154 }
155 }
156 }
157
158 private static ObjectId[] SelectEntitisInModelSpaceByViewport(
159 Document dwg, Point3dCollection boundaryInModelSpace)
160 {
161 ObjectId[] ids = null;
162
163 using (Transaction tran=dwg.TransactionManager.StartTransaction())
164 {
165 //Zoom to the extents of the viewport boundary in modelspace
166 //before calling Editor.SelectXxxxx()
167 ZoomToWindow(boundaryInModelSpace);
168
169 PromptSelectionResult res =
170 dwg.Editor.SelectCrossingPolygon(boundaryInModelSpace);
171 if (res.Status==PromptStatus.OK)
172 {
173 ids = res.Value.GetObjectIds();
174 }
175
176 //Restored to previous view (view before zoomming)
177 tran.Abort();
178 }
179
180 return ids;
181 }
182
183 private static void ZoomToWindow(Point3dCollection boundaryInModelSpace)
184 {
185 Extents3d ext =
186 GetViewportBoundaryExtentsInModelSpace(boundaryInModelSpace);
187
188 double[] p1 = new double[] { ext.MinPoint.X, ext.MinPoint.Y, 0.00 };
189 double[] p2 = new double[] { ext.MaxPoint.X, ext.MaxPoint.Y, 0.00 };
190
191 dynamic acadApp = Application.AcadApplication;
192 acadApp.ZoomWindow(p1, p2);
193 }
194
195 private static Extents3d GetViewportBoundaryExtentsInModelSpace(
196 Point3dCollection points)
197 {
198 Extents3d ext = new Extents3d();
199 foreach (Point3d p in points)
200 {
201 ext.AddPoint(p);
202 }
203
204 return ext;
205 }
206
207 private static ObjectId PickEntity(Editor ed)
208 {
209 PromptEntityOptions opt =
210 new PromptEntityOptions("\nSelect an entity:");
211 PromptEntityResult res = ed.GetEntity(opt);
212 if (res.Status==PromptStatus.OK)
213 {
214 return res.ObjectId;
215 }
216 else
217 {
218 return ObjectId.Null;
219 }
220 }
221
222 private static bool IsEntityInsideViewportBoundary(
223 Document dwg, ObjectId entId, Point3dCollection boundaryInModelSpace)
224 {
225 bool inside = false;
226 using (Transaction tran = dwg.TransactionManager.StartTransaction())
227 {
228 //Zoom to the extents of the viewport boundary in modelspace
229 //before calling Editor.SelectXxxxx()
230 ZoomToWindow(boundaryInModelSpace);
231
232 PromptSelectionResult res =
233 dwg.Editor.SelectCrossingPolygon(boundaryInModelSpace);
234 if (res.Status == PromptStatus.OK)
235 {
236 foreach (ObjectId id in res.Value.GetObjectIds())
237 {
238 if (id==entId)
239 {
240 inside = true;
241 break;
242 }
243 }
244 }
245
246 //Restored to previous view (before zoomming)
247 tran.Abort();
248 }
249
250 return inside;
251 }
252 }
253 }
Following picture shows the drawing I test the code against:
Update on 2019-12-27:
Code has been updated, referring to latest comment below. Grayed lines were removed, yellowed lines were replacement.
Nice work.
ReplyDeletethat is useful code. I had to do that to find what xref blocks were visible in vp's for a prog, so could not use the select function of editor. I used a function that checked if a point was inside a polyline shape (not an actual pline, the geometry of one. That would be slower to test on lines and arcs or text crossing, and have not needed that yet. Fast selection of entities without drawing open in the editor remains and issue...
ReplyDeleteThis Is exactly what I needed - you made my day. Side note, line 152 from "Create coordinate transform matrix" should read:
ReplyDelete152 else if (zaxis.Z < 0)
not
152 else if (zaxis.Z < 0)
Thanks again
Argh. I meant to point out that you don't have the smaller than symbol showing on line 152, but the html markup
ReplyDeleteNorman, I know this is an old post, but I am having a heap of trouble with polygonal viewports and in particular line 47 and the Polyline2d part of the viewport.
ReplyDeleteI am intently curious about the reason for trying to create a Polyline 2d. Any attempt to convert from the polyline to a Polyline2d fails...
Sorry for the late respond: I was travelling in middle of November and somehow missed your comment.
ReplyDeleteI was also puzzled when I looked at the code when prompted by your question: why did I use Polyline2d, instead of Polyline then? and clearly the code worked then. Maybe the clip polygon was Polyline2d in older Acad version? (at that time I probably was using Acad2012).
I updated the code to get the clip polygon as Polyline.
ReplyDeletePlease, do you have an example, moving objects from the model space to a specific viewport keeping its scale and vice versa?
This was lovvely to read
ReplyDelete