@itwin/core-frontend
Version:
iTwin.js frontend components
188 lines • 8.51 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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