在 Forge Viewer 裏切換模型視圖(Viewables)

此篇文章原做是 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)
相關文章
相關標籤/搜索