05/24/2012

Set crop box of 3d view that exactly fits an element

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.

publicIExternalCommand.Result Execute(

ExternalCommandData commandData,

refstring 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";

returnIExternalCommand.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;

returnIExternalCommand.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.

Comments

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.

publicIExternalCommand.Result Execute(

ExternalCommandData commandData,

refstring 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";

returnIExternalCommand.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;

returnIExternalCommand.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.