How to make Top view the active viewport and zoom to extents in C++ SDK

@dale,

I want to make the active viewport Top and turn off ConstructionAxes, ConstructionGrid, WorldAxes and maximize this view. So far I have the following working C++ code that does everything except set the active viewport to Top:

CRhinoView* view = RhinoApp().ActiveView();
CRhinoViewport& viewport = view->ActiveViewport();
viewport.SetShowConstructionAxes(false);
viewport.SetShowConstructionGrid(false);
viewport.SetShowWorldAxes(false);
const CDisplayPipelineAttributes* pStdAttrs = CRhinoDisplayAttrsMgr::StdShadedAttrs();
if (pStdAttrs) {
	viewport.SetDisplayMode(pStdAttrs->Id());
	view->MaximizeRestoreView();
	view->Redraw();
}

So I then tried this code to limit the changes to only the Top viewport:

CRhinoDoc *pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
ON_SimpleArray<CRhinoView*> view_list;
pDoc->GetViewList(view_list, true, false);
int32_t j;
for (j = 0; j < view_list.Count(); j++) {
	CRhinoView *view = view_list[j];
	ON_wString view_name = view->ActiveViewport().Name();
	const wchar_t *name = static_cast<const wchar_t*>(view_name);
	if (name == L"Top") {
		RhinoApp().SetActiveView(view);
		CRhinoViewport& viewport = view->ActiveViewport();
		viewport.SetShowConstructionAxes(false);
		viewport.SetShowConstructionGrid(false);
		viewport.SetShowWorldAxes(false);
		const CDisplayPipelineAttributes* pStdAttrs = CRhinoDisplayAttrsMgr::StdShadedAttrs();
		if (pStdAttrs) {
			viewport.SetDisplayMode(pStdAttrs->Id());
			view->MaximizeRestoreView();
			view->Redraw();
			break;
		}
	}
}

But the compare seem to not be working. Any suggestion?

In creating this C++ code I worked mostly from examples Dale has created but apparently I have not found a way to combine them. My Python code for all this works fine. It is just the C++ replacement that is giving me a headache.

Regards,
Terry.

Hi @Terry_Chappell,

When comparing strings use ON_wString:: CompareOrdinal.

bool bStringsAreEqual = name.CompareOrdinal(L"Top", true) == 0;
if (bStringsAreEqual)
{
  //...
}

– Dale

1 Like

Dale,

Thanks for the pointer which I fixed up a bit:

if (name.CompareOrdinal(L"Top", true) != -1))
{
  //...
}

One last question:
Once I have the view maximized, how do I do a Zoom Extents in C++ so I can see all of the mesh I created?

Regards,
Terry.

Hi @Terry_Chappell,

See the following sample:

cmdSampleZoomExtents.cpp

– Dale

Dale,

Here is the final code. I investigated your cmdSampleZoomExtents example and, motivated by it, I found a much faster way to zoom extends for my case of a mesh where I already have its min/max values. These can can be used to construct a bounding box that works well with the viewport method of viewport.DollyExtents(bBox, ON::world_cs); The new code does all the view adjustments I wanted along with zoom extents in 23 ms on the meshes I have checked (1M to 100M faces). Using RhinoDollyExtents from cmdSampleZoomExtents, takes 485 ms on my 100 M face mesh test case, which makes sense as it has to search thru the 50M vertices of the mesh in order to find the min/max XYZ values. However using RhinoDollyExtents seems like a good choice when the min/max values are not available.

NOTE: For the code here I created the pointer to the Rhino Document, pDoc, from its RuntimeSerialNumber passed from my Python script as a uint32_t using:
CRhinoDoc *pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
Other methods can also be used.

// Function that checks that view has camera orientation of a Top view.
bool is_top_view(CRhinoView *view) {
	const ON_Viewport vp = view->ActiveViewport().VP(); ON_3dVector dir = vp.CameraDirection();
	return (vp.IsParallelProjection() && dir.x == 0.0 && dir.y == 0.0 && dir.z < 0.0);
}
// Removed grid/axes, zoom extents, use shaded view and maximize Top viewport,
ON_SimpleArray<CRhinoView*> view_list;
// Get attributes for shaded view.
const CDisplayPipelineAttributes *shaded_attrib = CRhinoDisplayAttrsMgr::StdShadedAttrs();
// Construct bounding box 5% bigger than values in mm.
ON_BoundingBox bBox(1.05f*ON_3fPoint(mm[0], mm[2], mm[4]), 1.05f*ON_3fPoint(mm[1], mm[3], mm[5]));
// Get list of views in Rhino document and process each view.
int32_t i, num_views = pDoc->GetViewList(view_list, true, false);
for (i = 0; i < num_views; i++) {
	CRhinoView *view = view_list[i]; if (nullptr == view) continue;
	CRhinoViewport &viewport = view->ActiveViewport();
	viewport.SetShowConstructionGrid(false);
    viewport.SetShowConstructionAxes(false);
	viewport.SetShowWorldAxes(false);
	viewport.SetDisplayMode(shaded_attrib->Id());
	viewport.DollyExtents(bBox, ON::world_cs);
	if (is_top_view(view) && !view->IsMaximized()) view->MaximizeRestoreView(); 
}

With your help I was able to create code that exceeded by expectations. Thank you!

Regards,
Terry.

Hi @Terry_Chappell,

You can simplify your bounding box creation by doing this:

for (int i = 0; i < _countof(mm); i++)
  mm[i] *= 1.5;

ON_BoundingBox bbox(
  ON_3dPoint(mm[0], mm[1], mm[2]),
  ON_3dPoint(mm[3], mm[4], mm[5])
  );

// . . . 

– Dale

Dale,

I incorporated your nice suggestion for simplifying bBox generation using its min/max corners. This saves several lines of code and gets rid of the new/delete mishmash. My mm list comes from a C-types list passed from Python so it is not have a function template “_countof_helper” so I just used 6 for the loop count.

The code is updated in my post above. It runs a tiny bit faster. Thanks again, I just love nice code that does good work for me.

Regards,
Terry.

Dale,

I did some more work on cleaning up the code and improving its performance. It is updated in my post above and is really a screamer in terms of performance now, taking only 50 us to remove grid & axes plus zoom extents in all views and then make the Top view active and maximize it. Way faster than I ever imagined. An amazing result on the amazing day of Wed 6 Jan 2021.

Regards,
Terry.

Hi,@Terry
I have read some of your topics and know that you are very strict in terms of code and performance.
I have a small question.
If you use the program yourself, the view name is correct because you haven’t modified it. However, if the user is using it, problems may occur. For example, if I change the view name to “MyTop”, the code will not work.
If you judge the viewport by name, it can easily cause problems. I think it would be better to use camera orientation. I don’t know what others are using. I use camera orientation, it will be more versatile.
-Easy

1 Like

Easy,

You make a good point. In my case this code is the last part of a long Python/C++ script to import the .OBJ file of a mesh quickly. It is focused on importing 3D models built from Drone photos which have large XY extents and a much smaller Z extent so they view well in the default Top view. Now I think what you are saying is that if I were to look at the camera orientation of the this Top view I would find that it looks down on an XY plane. So I could use that to decide which view to maximize. And I could use its absence to trigger the creation of a new view that does this job. Is this what you are pointing out?

I have never looked at camera orientation before. Do have any code snippets to get me started?

Regards,
Terry.


Refer to that topic, because the name of the view can be set manually, if you only judge the view name “Top”, it may cause errors, so you need another way to judge the view “Top”. Dale mentioned the method in that topic. You can refer to it. Do you need C++ example?

Easy, @dale,

I tried translating the IsRhinoViewTop function to C++ but got stuck on two lines, one uses IsParallelProjection and the other CameraDirection for which I can find not counterparts in viewport. I do see these in ON_Viewport Class but do not know how to utilize them in my translation of the function:

bool IsRhinoViewTop(CRhinoView *view) {
	bool rc = false;
	CRhinoViewport &viewport = view->ActiveViewport();
	if (view != nullptr && viewport.IsParallelProjection()) {
		ON_3dVector top_dir = ON_3dVector(0.0, 0.0, -1.0);
		ON_3dVector cam_dir =viewport.CameraDirection();
		cam_dir.Unitize();
		if (top_dir.IsParallelTo(cam_dir)) {
			rc = true;
		}
	}
    return rc;
}

Any help appreciated.

Regards,
Terry.

try this.
note:I haven’t tested it yet. It should be OK.

	bool IsRhinoViewTop(CRhinoView * view) {
		bool rc = false;
		const ON_Viewport vp = view->ActiveViewport().VP();
		if (!vp.IsParallelProjection())  //edit: add '!'
			return rc;
		ON_3dVector cam_dir = vp.CameraDirection();
		cam_dir.Unitize();
		ON_3dVector top_dir(0.0, 0.0, -1.0);
		if (top_dir.IsParallelTo(cam_dir) != 0)
			return true;
		return rc;
	}

Easy,

It works with only one change, a ! in front of vp.IsParallelProjection:

bool IsRhinoViewTop(CRhinoView * view) {
	bool rc = false;
	const ON_Viewport vp = view->ActiveViewport().VP();
	if (!vp.IsParallelProjection()) { return rc; }
	ON_3dVector cam_dir = vp.CameraDirection();	cam_dir.Unitize();
	ON_3dVector top_dir(0.0, 0.0, -1.0);
	if (top_dir.IsParallelTo(cam_dir) != 0) { return true; }
	return rc;
}

Regards,
Terry.

:grinning: I was careless

  int IsParallelTo( 
        // returns  1: this and other vectors are parallel
        //         -1: this and other vectors are anti-parallel
        //          0: this and other vectors are not parallel
        //             or at least one of the vectors is zero
        const ON_3dVector& other,                           // other vector     
        double angle_tolerance = ON_DEFAULT_ANGLE_TOLERANCE // optional angle tolerance (radians)
        ) const;
//note
if (top_dir.IsParallelTo(cam_dir) != 0)  // top or bottom

Easy,

No worries mate, as they say in Australia. I read up about IsParallelTo so I was confident in the change. I will now put this additional code for IsRhinoViewTop into my post above.

Of course if there is no viewport with the camera orientation of a Top view, then no window will be maximized. But nothing breaks; it just makes the user do more work if they want one of their non-Top viewports to be maximized.

Regards,
Terry.

@Easy,

What purpose does the cam_dir.Unitize() serve? The function seems to work without it. Is it somehow better to feed unitized vectors to IsParallelTo ? I am not one to put in extra code if it is not needed.

Regards,
Terry.

Since a Top view should look down, I simplified the camera direction test to check for X & Y = 0 and Z negative. With this change the final code is below. Note that maximizing the Top view also makes it the active view. I use this code at the end of my Python/C++ script for importing the .OBJ file of a mesh. The Python script calls the DLL containing this C++ code and passes the doc.RuntimeSerialNumber so that the Rhino Document I am working on can be accessed in the C++ code. Other methods can also be used. If the min/max values of the object being displayed are not available, the slower alternative: RhinoDollyExtends(view) can be used. It is slower because it has to find the bounding box of the object; instead of running in 22 ms it takes 124 ms for a 5M face mesh. This is the time reported by timers imbedded in the code; it takes another 5 sec before the image appears due to the relatively slow Rhino display code.

// Get Rhino doc from RunTimeSerialNumber passed as uint32_t
CRhinoDoc *pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
// Make list for holding views, and get attributes for shaded view.
ON_SimpleArray<CRhinoView*> view_list;
const CDisplayPipelineAttributes *shaded_attrib = CRhinoDisplayAttrsMgr::StdShadedAttrs();
// Construct 5% bigger bounding box from mm = [xmin,xmax,ymin,ymax,zmin,zmax].
ON_BoundingBox bBox(ON_3fPoint(mm[0],mm[2],mm[4]), ON_3fPoint(mm[1],mm[3],mm[5]));
const ON_3dPoint &center = ON_3dPoint(0.5*(mm[1]+mm[0]), 0.5*(mm[3]+mm[2]), 0.5*(mm[5]+mm[4]));
bBox.Transform(ON_Xform::ScaleTransformation(center, 1.05));
// Get list of views in Rhino document.
int32_t num_views = pDoc->GetViewList(view_list, true, false);
// Removed grid/axes, switch to shaded view, zoom extents and maximize Top view.
for (int i = 0; i < num_views; i++) {
	CRhinoView *view = view_list[i]; if (view == nullptr) continue;
	CRhinoViewport &viewport = view->ActiveViewport();
	viewport.SetShowConstructionGrid(false);
	viewport.SetShowConstructionAxes(false);
	viewport.SetShowWorldAxes(false);
	viewport.SetDisplayMode(shaded_attrib->Id());
	viewport.DollyExtents(bBox, ON::world_cs); // Alt: RhinoDollyExtents(view)
	const ON_Viewport vp = viewport.VP(); ON_3dVector dir = vp.CameraDirection();
	// Top view has parallel projection and the camera direction is down.
	if (vp.IsParallelProjection() && dir.x == 0. && dir.y == 0. && dir.z < 0.) {
		if (!view->IsMaximized()) view->MaximizeRestoreView();
	}
}

Regards,
Terry.