Bentley Communities
Bentley Communities
  • Site
  • User
  • Site
  • Search
  • User
  • Welcome
  • Products
  • Support
  • About
  • More
  • Cancel
中国BDN社区
  • Welcome to Bentley Communities
  • Bentley's Communities
  • 中国BDN社区
  • Cancel
中国BDN社区
中国BDN社区-威客 imodel图形显示简单介绍
    • Sign In
    • 中国BDN社区-威客
    • -iTwin.js编程
      • iModel.js Front-End简介
      • iModel.js 样例项目配置与运行
      • iModel.js.UI开发基本介绍
      • iModel.js前端统一选择一致性机制
      • iModel.js基本查询
      • iModel.js若干前端功能开发样例
      • imodel图形显示简单介绍
      • iModel文件的合并等操作
      • Redux基本使用
      • simple-viewer-app源码简单分析
      • TypeScript基本介绍及环境配置
      • 下载视口截图
      • 关于imodel测量单位的转换
      • 关于如何导入与导出imodel中schema文件
      • 基于PresentationRules个性化显示iModel的内容
      • 如何在视窗内添加自定义装饰
      • 如何将2个bim中的图形模型显示在一个视图中
      • 用户选择保存感兴趣的视图
    • +MicroStation CONNECT版SDK的新变化
    • +MicroStation编程技巧
    • +OpenRoads中国版平台开发指南
    • +ORD SDK二次开发编程
    • +一步步学习ABD-CE开发
    • +一步步学习MicroStation CE Addin开发
    • 一步步学习MicroStation CE MDL开发
    • +一步步学习ProjectWise编程
    • 中国优先社区二次开发精华帖汇总
    • +学习Microstation交互式工具开发
    • +过期帖,留存仅供参考
    • +非Bentley社区分享的文章
    • C#、C/C++相关的编程知识汇总

     
     Questions about this article, topic, or product? Click here. 

    imodel图形显示简单介绍

    imodel图形显示简单介绍

    背景

    通过简单了解iModel中模型的显示过程,有助于我们理解并添加FeatureOverride等一些特性,从而实现一些需求。

    理解

    以视口V为例,基本过程如下所示:

    1. 清除与视口V当前场景关联的内容,并且让TileAdmin重置指定视口V的使用情况跟踪。
    2. 创建一个SceneContext的实例sc,该实例用于为视口V创建场景的上下文。其中,场景由在视口中可见的TileTree产生的一组RenderGraphic组成。
    3. 视口V的ViewState向sc实例填充内容,注意,与此同时也会添加用户自定义TiledGraphicsProvider的行为。
    4. RenderTarget的实例应用sc,以将V连接到WebGLRenderingContext并使视口的内容显示在屏幕上。

    其中视口V的ViewState如何向SceneContext的实例sc填充内容呢?

    以SpatialViewState为例,其中ViewState2d过程类似。

    我们知道每个SpatialViewState实例都维护着一个ModelSelectorState实例,其中包含着视口将要显示的Model的ID。然后,根据其所包含的ModelState(确切地说是GeometricModelState实例)开始调用createTileTreeReference以创建每个ModelState所需要的TileTreeReference的实例,然后利用TileTreeReference的实例将其引用的TileTree所产生的图像内容填充到sc实例中即可。

    大概流程如下所示:

    因此,也可以简单的理解为以下关系:

    应用

    这样的话,我们就可以通过自定义TiledGraphicsProvider以及FeatureOverrideProvider 从而实现一些功能。(即在对比不同的2个版本过程中,可以将图形中新增的,已删除,更改的元素分别以不同的颜色显示,以更直观的方式显示出2个版本之间的差异性)。

    以下是通过自定义TiledGraphicsProvider以及FeatureOverrideProvider 并结合上述思想实现的简单模拟版本对比功能,具体代码如下所示:

    import {
      SpatialModelTileTrees,
      TiledGraphicsProvider,
      FeatureOverrideProvider,
      Viewport,
      IModelApp,
      Tool,
      IModelConnection,
      FeatureSymbology,
      SpatialViewState,
      SnapshotConnection,
      TileTreeReference,
      ChangeFlags,
      SpatialModelState,
      TileTree,
    } from "@bentley/imodeljs-frontend";
    import { BeTimePoint, assert } from "@bentley/bentleyjs-core";
    import {
      FeatureAppearance,
      ColorDef,
      RgbColor,
    } from "@bentley/imodeljs-common";
    
    interface ChangedElems {
      deleted: Set<string>;
      inserted: Set<string>;
      updated: Set<string>;
    }
    export class VersionComparisonTool extends Tool {
      public static toolId = "VersionComparisonTool";
      public run(_args: any[]): boolean {
        const vp = IModelApp.viewManager.selectedView;
        if (undefined !== vp) {
          emulateVersionComparison(vp);
        }
        return true;
      }
    }
    class Trees extends SpatialModelTileTrees {
      private readonly _provider: Provider;
    
      public constructor(provider: Provider) {
        super(provider.viewport.view as SpatialViewState);
        this._provider = provider;
      }
    
      protected get _iModel() {
        return this._provider.iModel;
      }
    
      protected createTileTreeReference(
        model: SpatialModelState
      ): TileTreeReference | undefined {
        // ###TODO: If model contains no deleted elements, ignore
        return new Reference(
          model.createTileTreeReference(this._provider.viewport.view),
          this._provider
        );
      }
    }
    /** A proxy reference to a TileTreeReference originating from the secondary IModelConnection. */
    class Reference extends TileTreeReference {
      private readonly _ref: TileTreeReference;
      private readonly _provider: Provider;
    
      public constructor(ref: TileTreeReference, provider: Provider) {
        super();
        this._ref = ref;
        this._provider = provider;
      }
    
      public get castsShadows() {
        return this._ref.castsShadows;
      }
    
      public get treeOwner() {
        return this._ref.treeOwner;
      }
    
      protected getSymbologyOverrides(_tree: TileTree) {
        return this._provider.overrides;
      }
    }
    /** Returns true if version comparison is currently activated for the specified viewport. */
    export function isVersionComparisonEnabled(vp: Viewport): boolean {
      return undefined !== vp.findFeatureOverrideProviderOfType<Provider>(Provider);
    }
    export async function emulateVersionComparison(vp: Viewport): Promise<void> {
      if (isVersionComparisonEnabled(vp)) {
        await disableVersionComparison(vp);
        return;
      }
    
      await Provider.create(vp);
    }
    async function getElems(iModel: IModelConnection): Promise<Set<string>> {
      const elems = new Set<string>();
      const ecsql =
        "SELECT ECInstanceId FROM BisCore.SpatialElement WHERE GeometryStream IS NOT NULL";
      for await (const row of iModel.query(ecsql)) elems.add(row.id);
    
      return elems;
    }
    /** The most brain-dead, inefficient way of determining which elements exist only in one iModel or the other possible.
     * Any element present in only one or the other iModel is treated as an insertion or deletion.
     * Additionally, selection set is used as the set of updated elements.
     */
    async function determineChangedElems(
      iModel: IModelConnection,
      revision: IModelConnection
    ): Promise<ChangedElems> {
      const inserted = await getElems(iModel);
      const deleted = await getElems(revision);
    
      for (const elem of deleted) {
        if (inserted.has(elem)) {
          deleted.delete(elem);
          inserted.delete(elem);
        }
      }
    
      const updated = new Set<string>();
      for (const selected of iModel.selectionSet.elements)
        if (!inserted.has(selected) && !deleted.has(selected))
          updated.add(selected);
    
      iModel.selectionSet.emptyAll();
    
      return { inserted, deleted, updated };
    }
    const changedTransparency = 16; // NB: This will appear more transparent due to use of "fade-out" mode (flat alpha weight).
    const unchangedAppearance = FeatureAppearance.fromJSON({
      rgb: RgbColor.fromColorDef(ColorDef.blue),
      transparency: 0.7,
      nonLocatable: true,
    });
    /** Added to a Viewport to supply graphics from the secondary IModelConnection. */
    class Provider implements TiledGraphicsProvider, FeatureOverrideProvider {
      private readonly _trees: SpatialModelTileTrees;
      public readonly iModel: IModelConnection;
      public overrides: FeatureSymbology.Overrides;
      public readonly changedElems: ChangedElems;
      public readonly viewport: Viewport;
      private readonly _removals: Array<() => void> = [];
    
      public constructor(
        vp: Viewport,
        iModel: IModelConnection,
        elems: ChangedElems
      ) {
        this.iModel = iModel;
        this.changedElems = elems;
        this.viewport = vp;
    
        this._trees = new Trees(this);
        this.overrides = this.initOverrides();
    
        this._removals.push(
          vp.onViewportChanged.addListener((_vp, flags) =>
            this.handleViewportChanged(flags)
          )
        );
    
        vp.addTiledGraphicsProvider(this);
        vp.addFeatureOverrideProvider(this);
        vp.isFadeOutActive = true;
      }
    
      public dispose(): void {
        for (const removal of this._removals) removal();
    
        this._removals.length = 0;
    
        this.viewport.dropFeatureOverrideProvider(this);
        this.viewport.isFadeOutActive = false;
        this.viewport.dropTiledGraphicsProvider(this);
    
        // closing the iModel will do this - but let's not wait.
        this.iModel.tiles.purge(BeTimePoint.now());
    
        this.iModel.close(); // eslint-disable-line @typescript-eslint/no-floating-promises
      }
    
      public static async create(vp: Viewport): Promise<Provider | undefined> {
        try {
          const view = vp.view as SpatialViewState;
          assert(view.isSpatialView());
    
          // Open the "revision" iModel.
          // const filename = vp.iModel.getRpcProps().key + ".rev";
          const filename = vp.iModel.getRpcProps().key;
          const iModel = await SnapshotConnection.openFile(filename);
    
          // ###TODO determine which model(s) contain the deleted elements - don't need tiles for any others.
          await iModel.models.load(view.modelSelector.models);
    
          const changedElems = await determineChangedElems(vp.iModel, iModel);
    
          return new Provider(vp, iModel, changedElems);
        } catch (err) {
          alert(err.toString());
          return undefined;
        }
      }
    
      public forEachTileTreeRef(
        _vp: Viewport,
        func: (ref: TileTreeReference) => void
      ): void {
        this._trees.forEach(func);
      }
    
      /** The overrides applied to the *primary* IModelConnection, to hilite inserted/updated elements. */
      public addFeatureOverrides(
        overrides: FeatureSymbology.Overrides,
        _viewport: Viewport
      ): void {
        overrides.setDefaultOverrides(unchangedAppearance);
    
        for (const elem of this.changedElems.deleted) overrides.setNeverDrawn(elem);
    
        const inserted = FeatureAppearance.fromRgba(
          //   ColorDef.from(0, 0xff, 0, changedTransparency)
          ColorDef.red
        );
        for (const elem of this.changedElems.inserted)
          overrides.overrideElement(elem, inserted);
    
        const updated = FeatureAppearance.fromRgba(
          // ColorDef.from(0, 0x7f, 0xff, changedTransparency)
          ColorDef.blue
        );
        for (const elem of this.changedElems.updated)
          overrides.overrideElement(elem, updated);
      }
    
      /** The overrides applied to the tiles from the *secondary* IModelConnection, to draw only deleted elements. */
      private initOverrides(): FeatureSymbology.Overrides {
        const ovrs = new FeatureSymbology.Overrides(this.viewport);
        ovrs.neverDrawn.clear();
        ovrs.alwaysDrawn.clear();
        ovrs.setAlwaysDrawnSet(this.changedElems.deleted, true, false); // really "only-drawn" - only draw our deleted elements - unless their subcategory is turned off.
    
        const red = ColorDef.from(0xff, 0, 0, changedTransparency);
        ovrs.setDefaultOverrides(FeatureAppearance.fromRgba(red));
    
        return ovrs;
      }
    
      private handleViewportChanged(flags: ChangeFlags): void {
        if (flags.viewState && !this.viewport.view.isSpatialView()) {
          // Switched to a 2d view. Terminate version comparison.
          this.dispose();
          return;
        }
    
        if (flags.areFeatureOverridesDirty) {
          this.overrides = this.initOverrides();
          this.viewport.invalidateScene();
        }
    
        if (flags.viewedModels) {
          this._trees.markDirty();
          this.viewport.invalidateScene();
    
          const models = (this.viewport.view as SpatialViewState).modelSelector
            .models;
          const unloaded = this.iModel.models.filterLoaded(models);
          if (undefined === unloaded) return;
    
          this.iModel.models
            .load(unloaded)
            .then(() => {
              this._trees.markDirty();
              this.viewport.invalidateScene();
            })
            .catch((_) => undefined);
        }
      }
    }
    /** Turn off version comparison if it is enabled. */
    export async function disableVersionComparison(vp: Viewport): Promise<void> {
      const existing = vp.findFeatureOverrideProviderOfType<Provider>(Provider);
      if (undefined !== existing) {
        existing.dispose();
        await existing.iModel.close();
      }
    }
    

    补充

    其中,在从TileTreeReference获取其所引用的TileTree使用了较复杂的几个类层次并涉及地形,图像图层等,其关系简单罗列如下所示:

    • Share
    • History
    • More
    • Cancel
    • Devin Liu Created by Devin Liu
    • When: Tue, Sep 1 2020 3:12 AM
    • Devin Liu Last revision by Devin Liu
    • When: Tue, Sep 1 2020 3:17 AM
    • Revisions: 2
    • Comments: 0
    Recommended
    Related
    Communities
    • Home
    • Getting Started
    • Community Central
    • Products
    • Support
    • Secure File Upload
    • Feedback
    Support and Services
    • Home
    • Product Support
    • Downloads
    • Subscription Services Portal
    Training and Learning
    • Home
    • About Bentley Institute
    • My Learning History
    • Reference Books
    Social Media
    •    LinkedIn
    •    Facebook
    •    Twitter
    •    YouTube
    •    RSS Feed
    •    Email

    © 2023 Bentley Systems, Incorporated  |  Contact Us  |  Privacy |  Terms of Use  |  Cookies