此篇文章原作是 Autodesk ADN Philippe Leefsma,以下以我简称。
这有一个简易的博客用来说明一个我刚加入 https://forge-rcdb.autodesk.io 的一个新功能,他主要是提供一个使用介面让使用者可以在一个文档(Forge Document)里不同的视图(Forge Viewable)间快速的切换。A360 viewer有提供类似的功能,但这并不包含在 Forge Viewer 里头。
在 Forge Document 里。条列 Viewable 清单或是切换视图是非常容易的,你主要是要建立一个使用者介面让使用者去选取他们想观看的内容。如果您不知道要怎么让 Forge Model Derivative 服务帮您转换多个视图,请先看看这篇文章:为什么Revit模型有多个视图,丢到 Forge 转换后确只剩一个? (如何设定多个视图)
接著让我们来看看该怎么实作这个功能(我所有的代码都有用到 ES6、async/await,UI的部份使用了 React):
一、从已转换好的 URN 载入 Document:
/////////////////////////////////////////////////////////
// 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)
})
})
}
二、从已载入的 Document 物件获取所有 Viewable 清单:
/////////////////////////////////////////////////////////
// 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
}
三、载入已选中的 Viewable:
首先,我门要把已载入的模型先卸载,下面的样例是假设我们已经载入了一个模型,现在使用者选择了新的 Viewable
要 Forge Viewer 载入。我使用了 viewer.tearDown()
来确保当前载入模型占用的内存可以都被释出。这个样例也支持从 3D 模型切换到 2D 模型,或者是反过来;您可以忽略我代码里使用到的 React 相关的代码。
/////////////////////////////////////////////////////////
// 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
来体验。
/////////////////////////////////////////////////////////
// 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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。