此篇文章原做是 Autodesk ADN Philippe Leefsma,如下以我簡稱。javascript
這有一個簡易的博客用來講明一個我剛加入 https://forge-rcdb.autodesk.io 的一個新功能,他主要是提供一個使用介面讓使用者能夠在一個文檔(Forge Document)裏不一樣的視圖(Forge Viewable)間快速的切換。A360 viewer有提供相似的功能,但這並不包含在 Forge Viewer 裏頭。css
在 Forge Document 裏。條列 Viewable 清單或是切換視圖是很是容易的,你主要是要創建一個使用者介面讓使用者去選取他們想觀看的內容。若是您不知道要怎麼讓 Forge Model Derivative 服務幫您轉換多個視圖,請先看看這篇文章:爲何Revit模型有多個視圖,丟到 Forge 轉換後確只剩一個? (如何設定多個視圖)html
接著讓咱們來看看該怎麼實做這個功能(我全部的代碼都有用到 ES六、async/await,UI的部份使用了 React):java
1、從已轉換好的 URN 載入 Document:react
///////////////////////////////////////////////////////// // Load a document from URN // ///////////////////////////////////////////////////////// static loadDocument (urn) { return new Promise((resolve, reject) => { const paramUrn = !urn.startsWith('urn:') ? 'urn:' + urn : urn Autodesk.Viewing.Document.load(paramUrn, (doc) => { resolve (doc) }, (error) => { reject (error) }) }) }
2、從已載入的 Document 物件獲取全部 Viewable 清單:segmentfault
///////////////////////////////////////////////////////// // Return viewables // ///////////////////////////////////////////////////////// static getViewableItems (doc, roles = ['3d', '2d']) { const rootItem = doc.getRootItem() let items = [] const roleArray = roles ? (Array.isArray(roles) ? roles : [roles]) : [] roleArray.forEach((role) => { items = [ ...items, ...Autodesk.Viewing.Document.getSubItemsWithProperties( rootItem, { type: 'geometry', role }, true) ] }) return items }
3、載入已選中的 Viewable:
首先,我門要把已載入的模型先卸載,下面的樣例是假設咱們已經載入了一個模型,如今使用者選擇了新的 Viewable
要 Forge Viewer 載入。我使用了 viewer.tearDown()
來確保當前載入模型佔用的內存能夠都被釋出。這個樣例也支持從 3D 模型切換到 2D 模型,或者是反過來;您能夠忽略我代碼裏使用到的 React 相關的代碼。api
///////////////////////////////////////////////////////// // Load the selected viewable // ///////////////////////////////////////////////////////// onItemSelected (item) { const {activeItem} = this.react.getState() if (item.guid !== activeItem.guid) { this.viewer.tearDown() this.viewer.start() const path = this.viewerDocument.getViewablePath(item) this.viewer.loadModel(path) this.react.setState({ activeItem: item }) } }
完整的代碼以下所示,對這功能有興趣的朋友們能夠經過這個網址 https://forge-rcdb.autodesk.i...,並載入 Viewable Selector
來體驗。app
///////////////////////////////////////////////////////// // Viewing.Extension.ViewableSelector // by Philippe Leefsma, November 2017 // ///////////////////////////////////////////////////////// import MultiModelExtensionBase from 'Viewer.MultiModelExtensionBase' import './Viewing.Extension.ViewableSelector.scss' import WidgetContainer from 'WidgetContainer' import ReactTooltip from 'react-tooltip' import ServiceManager from 'SvcManager' import Toolkit from 'Viewer.Toolkit' import ReactDOM from 'react-dom' import Image from 'Image' import Label from 'Label' import React from 'react' class ViewableSelectorExtension extends MultiModelExtensionBase { ///////////////////////////////////////////////////////// // Class constructor // ///////////////////////////////////////////////////////// constructor (viewer, options) { super (viewer, options) this.react = options.react } ///////////////////////////////////////////////////////// // // ///////////////////////////////////////////////////////// get className() { return 'viewable-selector' } ///////////////////////////////////////////////////////// // Extension Id // ///////////////////////////////////////////////////////// static get ExtensionId() { return 'Viewing.Extension.ViewableSelector' } ///////////////////////////////////////////////////////// // Load callback // ///////////////////////////////////////////////////////// load () { this.react.setState({ activeItem: null, items: [] }).then (async() => { const urn = this.options.model.urn this.viewerDocument = await this.options.loadDocument(urn) const items = await Toolkit.getViewableItems( this.viewerDocument) if (items.length > 1) { this.createButton() await this.react.setState({ activeItem: items[0], items: [items[0], items[1], items[2]] }) if (this.options.showPanel) { this.showPanel (true) } } }) console.log('Viewing.Extension.ViewableSelector loaded') return true } ///////////////////////////////////////////////////////// // Unload callback // ///////////////////////////////////////////////////////// unload () { this.react.popViewerPanel(this) console.log('Viewing.Extension.ViewableSelector unloaded') return true } ///////////////////////////////////////////////////////// // Load the selected viewable // ///////////////////////////////////////////////////////// onItemSelected (item) { const {activeItem} = this.react.getState() if (item.guid !== activeItem.guid) { this.viewer.tearDown() this.viewer.start() const path = this.viewerDocument.getViewablePath(item) this.viewer.loadModel(path) this.react.setState({ activeItem: item }) } } ///////////////////////////////////////////////////////// // Create a button to display the panel // ///////////////////////////////////////////////////////// createButton () { this.button = document.createElement('button') this.button.title = 'This model has multiple views ...' this.button.className = 'viewable-selector btn' this.button.innerHTML = 'Views' this.button.onclick = () => { this.showPanel(true) } const span = document.createElement('span') span.className = 'fa fa-list-ul' this.button.appendChild(span) this.viewer.container.appendChild(this.button) } ///////////////////////////////////////////////////////// // Show/Hide panel // ///////////////////////////////////////////////////////// showPanel (show) { if (show) { const {items} = this.react.getState() this.button.classList.add('active') const container = this.viewer.container const height = Math.min( container.offsetHeight - 110, (items.length + 1) * 78 + 55) this.react.pushViewerPanel(this, { maxHeight: height, draggable: false, maxWidth: 500, minWidth: 310, width: 310, top: 30, height }) } else { this.react.popViewerPanel(this.id).then(() => { this.button.classList.remove('active') }) } } ///////////////////////////////////////////////////////// // Render React panel content // ///////////////////////////////////////////////////////// renderContent () { const {activeItem, items} = this.react.getState() const urn = this.options.model.urn const apiUrl = this.options.apiUrl const domItems = items.map((item) => { const active = (item.guid === activeItem.guid) ? ' active' :'' const query = `size=400&guid=${item.guid}` const src = `${apiUrl}/thumbnails/${urn}?${query}` return ( <div key={item.guid} className={"item" + active} onClick={() => this.onItemSelected(item)}> <div className="image-container" data-for={`thumbnail-${item.guid}`} data-tip> <Image src={src}/> </div> <ReactTooltip id={`thumbnail-${item.guid}`} className="tooltip-thumbnail" delayShow={700} effect="solid" place="right"> <div> <img src={src} height="200"/> </div> </ReactTooltip> <Label text={item.name}/> </div> ) }) return ( <div className="items"> {domItems} <div style={{height: '80px'}}/> </div> ) } ///////////////////////////////////////////////////////// // Render title // ///////////////////////////////////////////////////////// renderTitle () { return ( <div className="title"> <label> Select Viewable </label> <div className="viewable-selector-controls"> <button onClick={() => this.showPanel(false)} title="Toggle panel"> <span className="fa fa-times"/> </button> </div> </div> ) } ///////////////////////////////////////////////////////// // Render main // ///////////////////////////////////////////////////////// render (opts) { return ( <WidgetContainer renderTitle={() => this.renderTitle(opts.docked)} showTitle={opts.showTitle} className={this.className}> {this.renderContent()} </WidgetContainer> ) } } Autodesk.Viewing.theExtensionManager.registerExtension( ViewableSelectorExtension.ExtensionId, ViewableSelectorExtension)