private void ApplyViewOrientationAndVisibility(UIDocument uiDocument, View3D view, Camera camera) { using var trans = new Transaction(uiDocument.Document); if (trans.Start($"Apply view orientation and visibility in '{view.Name}'") != TransactionStatus.Started) { return; } StatusBarService.SetStatusText("Loading view point data ..."); Log.Information("Calculating view orientation from camera position ..."); ProjectPosition projectPosition = uiDocument.Document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero); var viewOrientation3D = RevitUtils.TransformCameraPosition( new ProjectPositionWrapper(projectPosition), camera.Position.ToInternalUnits(), true) .ToViewOrientation3D(); if (camera.Type == CameraType.Perspective) { Log.Information("Setting active far viewer bound to zero ..."); Parameter farClip = view.get_Parameter(BuiltInParameter.VIEWER_BOUND_ACTIVE_FAR); if (farClip.HasValue) { farClip.Set(0); } } Log.Information("Applying new view orientation ..."); view.SetOrientation(viewOrientation3D); Log.Information("Applying element visibility ..."); var currentlyVisibleElements = uiDocument.Document.GetVisibleElementsOfView(view); var map = uiDocument.Document.GetIfcGuidElementIdMap(currentlyVisibleElements); var exceptionElements = GetViewpointVisibilityExceptions(map); var selectedElements = GetViewpointSelection(map); if (exceptionElements.Any()) { if (_bcfViewpoint.GetVisibilityDefault()) { view.HideElementsTemporary(exceptionElements); selectedElements = selectedElements.Where(id => !exceptionElements.Contains(id)).ToList(); } else { view.IsolateElementsTemporary(exceptionElements); selectedElements = selectedElements.Where(id => exceptionElements.Contains(id)).ToList(); } } view.ConvertTemporaryHideIsolateToPermanent(); if (selectedElements.Any()) { Log.Information("Select {n} elements ...", selectedElements.Count); uiDocument.Selection.SetElementIds(selectedElements); } trans.Commit(); }
/// <summary> /// Zoom the view to the correct scale, if necessary. /// </summary> /// <remarks>In Revit, orthogonal views do not change their camera positions, when zooming in or out. Hence, /// the values stored in the BCF viewpoint are not sufficient to restore the previously exported viewpoint. /// In order to get correct zooming, the scale value (view box height) is used, to calculate the correct zoom /// corners according to view center. /// See https://thebuildingcoder.typepad.com/blog/2020/10/save-and-restore-3d-view-camera-settings.html /// </remarks> private static void ZoomIfNeeded(UIApplication app, Camera camera, ElementId viewId) { if (camera.Type != CameraType.Orthogonal || camera is not OrthogonalCamera orthoCam) { return; } Log.Information("Found orthogonal camera, setting zoom callback ..."); StatusBarService.SetStatusText("Waiting for view to render to apply zoom ..."); AppIdlingCallbackListener.SetPendingZoomChangedCallback(app, viewId, orthoCam.ViewToWorldScale); }
/// <summary> /// Sets a callback that applies a zoom to the current view. /// </summary> /// <param name="app">The current UI application.</param> /// <param name="viewId">The view ID for the view to be zoomed.</param> /// <param name="zoom">The zoom in decimal precision.</param> public static void SetPendingZoomChangedCallback(UIApplication app, ElementId viewId, decimal zoom) { void Callback(object sender, IdlingEventArgs args) { StatusBarService.SetStatusText("Zooming to scale '" + zoom + "' ..."); UIView currentView = app.ActiveUIDocument.GetOpenUIViews().First(); if (currentView.ViewId != viewId) { return; } UIDocument uiDoc = app.ActiveUIDocument; View activeView = uiDoc.ActiveView; var zoomCorners = currentView.GetZoomCorners(); XYZ bottomLeft = zoomCorners[0]; XYZ topRight = zoomCorners[1]; var(currentHeight, currentWidth) = RevitUtils.ConvertToViewBoxValues(topRight, bottomLeft, activeView.RightDirection); var zoomedViewBoxHeight = Convert.ToDouble(zoom).ToInternalRevitUnit(); var zoomedViewBoxWidth = zoomedViewBoxHeight * currentWidth / currentHeight; XYZ newTopRight = activeView.Origin .Add(activeView.UpDirection.Multiply(zoomedViewBoxHeight / 2)) .Add(activeView.RightDirection.Multiply(zoomedViewBoxWidth / 2)); XYZ newBottomLeft = activeView.Origin .Subtract(activeView.UpDirection.Multiply(zoomedViewBoxHeight / 2)) .Subtract(activeView.RightDirection.Multiply(zoomedViewBoxWidth / 2)); Log.Information("Zoom to {topRight} | {bottomLeft} ...", newTopRight.ToString(), newBottomLeft.ToString()); currentView.ZoomAndCenterRectangle(newTopRight, newBottomLeft); StatusBarService.ResetStatusBarText(); Log.Information("Finished applying zoom for orthogonal view."); app.Idling -= Callback; } Log.Information("Append zoom callback for orthogonal view to idle state of Revit application ..."); app.Idling += Callback; }