By Joe Ye
I want to set the crop box of a 3d view to correspond to a single room in plan. I have already been successful in setting the crop box in plan but want to find a similar view in 3d. It won't be exactly the same since the view is angled but I'd like to get to something close.
Solution
In the purpose of setting the crop box for 3D view, we need to get bottom-left and up-right points of a box that exactly encloses the room.
First we get all the vertices of the room in WCS, and project all the vertices to the 3D view. The projection can be done by means of the inverse transform returned by View3D.CropBox.Transform.
Then find the bottom-left point and the up-right point in these converted points.
Finally assign the two points to the CropBox property.
Please see the code of the command. It changes the crop box to show only a room in active document. Before run this command, please make the 3D view as active view.
public IExternalCommand.Result Execute(
ExternalCommandData commandData,
ref string messages, ElementSet elements)
{
Application app = commandData.Application;
Document doc = app.ActiveDocument;
//get the 3d view's cropbox .
//Please set 3d view as current view
//before run this command
View3D view3d = doc.ActiveView as View3D;
geom.BoundingBoxXYZ bounding = view3d.CropBox;
geom.Transform transform = bounding.Transform;
//get the transform from the 3d model to current view
geom.Transform transformInverse = transform.Inverse;
//get all a room
ElementIterator ei = doc.get_Elements(typeof(Room));
Room room1 = null;
while (ei.MoveNext())
{
room1 = ei.Current as Room;
if (room1 != null)
{
break;
}
}
if (room1 == null)
{
messages = "No room in the current document";
return IExternalCommand.Result.Failed;
}
geom.XYZArray xyzvertex = app.Create.NewXYZArray();
//get the bounding box of the room.
geom.Element gElem = room1.ClosedShell;
foreach (geom.GeometryObject gObj in gElem.Objects)
{
if (gObj is geom.Solid)
{
//get all the edges in it
geom.Solid solid = gObj as geom.Solid;
foreach (geom.Edge gEdge in solid.Edges)
{
geom.XYZArray xyzArray = gEdge.Tessellate();
foreach (geom.XYZ pt in xyzArray)
{
//Collect all the vertex.
//There are duplicate points in it.
//For simplicity, we don't exclude them.
xyzVertex.Append(pt);
}
}
}
}
geom.XYZArray xyzvertex3dView = app.Create.NewXYZArray();
geom.XYZ ptWork = null;
foreach (geom.XYZ pt in xyzVertex)
{
ptWork = transformInverse.OfPoint(pt);
xyzvertex3dView.Append(ptWork);
}
//ingore the Z coorindates and find
//the max X , Y and Min X, Y in 3d view.
double dMaxX = 0, dMaxY = 0, dMinX = 0, dMinY = 0;
//geom.XYZ ptMaxX, ptMaxY, ptMinX,ptMInY;
//coorresponding point.
bool bFirstPt = true;
foreach (geom.XYZ pt1 in xyzvertex3dView)
{
if (true == bFirstPt)
{
dMaxX = pt1.X;
dMaxY = pt1.Y;
dMinX = pt1.X;
dMinY = pt1.Y;
bFirstPt = false;
}
else
{
if (dMaxX < pt1.X)
dMaxX = pt1.X;
if (dMaxY < pt1.Y)
dMaxY = pt1.Y;
if (dMinX > pt1.X)
dMinX = pt1.X;
if (dMinY > pt1.Y)
dMinY = pt1.Y;
}
}
bounding.Max = new geom.XYZ(dMaxX, dMaxY, bounding.Max.Z);
bounding.Min = new geom.XYZ(dMinX, dMinY, bounding.Min.Z);
view3d.CropBox = bounding;
view3d.CropBoxActive = true;
view3d.CropBoxVisible = true;
return IExternalCommand.Result.Succeeded;
}
In this sample code, we take a room as an example. If you want to do this for other element, you can directly get the vertex of the element by Element.Boundingbox. The other steps are the same.