UNPKG

@itwin/core-frontend

Version:
188 lines • 8.51 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Views */ import { CompressedId64Set, Id64 } from "@itwin/core-bentley"; import { Camera, Code, Environment, IModel, IModelReadRpcInterface, RenderMode, } from "@itwin/core-common"; import { Range3d } from "@itwin/core-geometry"; import { SpatialViewState } from "./SpatialViewState"; /** * API for creating a 3D default [[ViewState3d]] for an iModel. @see [[ViewCreator2d]] to create a view for a 2d model. * Example usage: * ```ts * const viewCreator = new ViewCreator3d(imodel); * const defaultView = await viewCreator.createDefaultView({skyboxOn: true}); * ``` * @public * @extensions */ export class ViewCreator3d { _imodel; /** * Constructs a ViewCreator3d using an [[IModelConnection]]. * @param _imodel [[IModelConnection]] to query for categories and/or models. */ constructor(_imodel) { this._imodel = _imodel; } /** * Creates a default [[ViewState3d]] based on the model ids passed in. If no model ids are passed in, all 3D models in the iModel are used. * @param [options] Options for creating the view. * @param [modelIds] Ids of models to display in the view. * @throws [IModelError]($common) If no 3d models are found in the iModel. */ async createDefaultView(options, modelIds) { const rpcOptions = modelIds ? { modelIds: CompressedId64Set.sortAndCompress(modelIds) } : {}; const rpc = IModelReadRpcInterface.getClientForRouting(this._imodel.routingContext.token); const serializedProps = await rpc.getCustomViewState3dData(this._imodel.getRpcProps(), rpcOptions); const baseExtents = Range3d.fromJSON(serializedProps.modelExtents); const props = await this._createViewStateProps(CompressedId64Set.decompressArray(serializedProps.modelIds), CompressedId64Set.decompressArray(serializedProps.categoryIds), baseExtents, options); const viewState = SpatialViewState.createFromProps(props, this._imodel); try { await viewState.iModel.subcategories.loadAllUsedSpatialSubCategories(); await viewState.load(); } catch { } if (options?.standardViewId) viewState.setStandardRotation(options.standardViewId); if (options?.allSubCategoriesVisible) viewState.displayStyle.enableAllLoadedSubCategories(viewState.categorySelector.categories); const range = viewState.computeFitRange({ baseExtents }); viewState.lookAtVolume(range, options?.vpAspect); return viewState; } /** * Generates a view state props object for creating a view. Merges display styles with a seed view if the options.useSeedView is true * @param models Models to put in view props * @param options view creation options like camera On and skybox On */ async _createViewStateProps(models, categories, modelExtents, options) { // Use dictionary model in all props const dictionaryId = IModel.dictionaryId; if (modelExtents.isNull) modelExtents.setFrom(this._imodel.projectExtents); let originX = modelExtents.low.x; let originY = modelExtents.low.y; const originZ = modelExtents.low.z; let deltaX = modelExtents.xLength(); let deltaY = modelExtents.yLength(); const deltaZ = modelExtents.zLength(); // if vp aspect given, update model extents to fit view if (options?.vpAspect) { const modelAspect = deltaY / deltaX; if (modelAspect > options.vpAspect) { const xFix = deltaY / options.vpAspect; originX = originX - xFix / 2; deltaX = deltaX + xFix; } else if (modelAspect < options.vpAspect) { const yFix = deltaX * options.vpAspect; originY = originY - yFix / 2; deltaY = deltaY + yFix; } } const categorySelectorProps = { categories, code: Code.createEmpty(), model: dictionaryId, classFullName: "BisCore:CategorySelector", }; const modelSelectorProps = { models, code: Code.createEmpty(), model: dictionaryId, classFullName: "BisCore:ModelSelector", }; const cameraData = new Camera(); const cameraOn = options?.cameraOn !== false; const viewDefinitionProps = { categorySelectorId: "", displayStyleId: "", code: Code.createEmpty(), model: dictionaryId, origin: { x: originX, y: originY, z: originZ }, extents: { x: deltaX, y: deltaY, z: deltaZ }, classFullName: "BisCore:SpatialViewDefinition", cameraOn, camera: { lens: cameraData.lens.toJSON(), focusDist: cameraData.focusDist, eye: cameraData.eye.toJSON(), }, }; const displayStyleProps = { code: Code.createEmpty(), model: dictionaryId, classFullName: "BisCore:DisplayStyle3d", jsonProperties: { styles: { viewflags: { renderMode: RenderMode.SmoothShade, noSourceLights: false, noCameraLights: false, noSolarLight: false, noConstruct: true, noTransp: false, visEdges: false, backgroundMap: this._imodel.isGeoLocated, }, environment: options !== undefined && options.skyboxOn !== undefined && options.skyboxOn ? Environment.defaults.withDisplay({ sky: true }).toJSON() : undefined, }, }, }; const viewStateProps = { displayStyleProps, categorySelectorProps, modelSelectorProps, viewDefinitionProps, }; // merge seed view props if needed return options?.useSeedView ? this._mergeSeedView(viewStateProps) : viewStateProps; } /** * Merges a seed view in the iModel with the passed view state props. It will be a no-op if there are no default 3D views in the iModel * @param viewStateProps Input view props to be merged */ async _mergeSeedView(viewStateProps) { const viewId = await this._getDefaultViewId(); // Handle iModels without any default view id if (viewId === undefined) return viewStateProps; const seedViewState = await this._imodel.views.load(viewId); const seedViewStateProps = { categorySelectorProps: seedViewState.categorySelector.toJSON(), modelSelectorProps: seedViewState.modelSelector.toJSON(), viewDefinitionProps: seedViewState.toJSON(), displayStyleProps: seedViewState.displayStyle.toJSON(), }; const mergedDisplayProps = seedViewStateProps.displayStyleProps; if (mergedDisplayProps.jsonProperties !== undefined) { mergedDisplayProps.jsonProperties.styles = { ...mergedDisplayProps.jsonProperties.styles, ...viewStateProps.displayStyleProps.jsonProperties.styles, }; } return { ...seedViewStateProps, ...viewStateProps, displayStyleProps: mergedDisplayProps }; } /** * Get the Id of the default view. */ async _getDefaultViewId() { // eslint-disable-next-line @typescript-eslint/no-deprecated const viewId = await this._imodel.views.queryDefaultViewId(); if (viewId !== Id64.invalid) return viewId; // Return the first spatial view const viewList = await this._imodel.views.getViewList({ wantPrivate: false, limit: 1, from: SpatialViewState.classFullName }); return viewList.length === 0 ? undefined : viewList[0].id; } } //# sourceMappingURL=ViewCreator3d.js.map