10 May 2019

Consume AEC Data which are from Model Derivative API

Model Derivative API has been able to extract AEC data of Revit model such as level, grid, views, phase, linked documents  etc. And Forge Viewer also provides the corresponding methods

Follow @Xiaodong Liang

Model Derivative API extracts general data of the source model: hierarchy trees, object properties & selected geometries. In most cases, these data are sufficient when working with Forge Viewer. While for Revit file, there are some specific AEC data that will be also useful to integrate with the application, such as: level, grid, views, phase, linked documents etc. 

We have had some smart solutions. e.g. :

  • Level by tree node name: searching out the nodes of model tree whose name ‘level’, and isolate the objects of same level by sectioning
  • Grid by genetic model: create generic model to represent grid in Revit and display in Forge Viewer

From certain release, Model Derivative API has been able to extract the data of AEC workflow. And Forge Viewer also provides the corresponding methods to grab them. The data are not available in cache by default after loading the model in Viewer. So the first step is to explicitly call downloadAecModelData of viewer document. Next, getAecModelData of document node will tell the related information. One sample data is as below:

all

Most data are self-explained by their names. The following are notes for some data with the schema

grid

grids tells all grids data of host document and linked documents. document of each grid tells which document the gird comes from. One gird might be multi-segments,
each segment contains start and end point. It looks there is not yet flag to indicate which level the grids are adopted. All grids are in the same level.
 

level

levelOccluderIds: Lists all Floor and Ceiling entity ids. Unfortunately, it does not tell which ids belong to which level. 

Levels: Lists all available levels". It tells elevation of the level relative to the project base height , height the distance to the next level, name of a level. It also tells if a level is ground plane or not. 

phases

Phases: Lists all non-empty phases, which corresponds to what inside source Revit file.

viewpoints
Viewports: List of all sheets' viewports, including camera data, sectioning plane data, and guid of corresponding sheet etc. cameraOrientation array provides view camera orientation parameters. array[0]-array[2] is  camera forward direction vector; array[3]-array[5] is camera up vector; array[6]-array[8] is camera origin (eye point) coordinates. 
viewportPosition is the array of viewport placement position on the sheet. array[0]-array[2] is min point of viewport position (low left corner); array[3]-array[5] is max point of viewport position (top right corner).

I tried to play with grids data. The demo below produces grid line, circle and text by Three.js. The graphics text is from the codes of another blog

//first step: explicitly call downloadAecModelData of viewer document.
function onDocumentLoadSuccess(doc) { 

            var viewables = viewerApp1.bubble.search({'type':'geometry'});
            if (viewables.length === 0) {
                console.error('Document contains no viewables.');
                return;
            }
            
           //explicitly call downloadAecModelData of viewer document.
            doc.downloadAecModelData()

            //....
}

Define the extension of creating text (note, it looks the other blog makes a small mistake on defining createText in the extension Viewing.Extension.Text, I have fixed it in this blog) 

import { Font, TextGeometry} from './threejs-full-es6/builds/Three.es.js'
import FontJson from './helvetiker_bold.typeface.json'

export default class TextExtension
  extends Autodesk.Viewing.Extension {

  /////////////////////////////////////////////////////////
  // Adds a color material to the viewer
  //
  /////////////////////////////////////////////////////////
  constructor (viewer, options) {

    super()

    this.viewer = viewer
  }

  load () {

    return true
  }

  unload () {

    return true
  }

  /////////////////////////////////////////////////////////
  // Adds a color material to the viewer
  //
  /////////////////////////////////////////////////////////
  createColorMaterial (color) {

    const material = new THREE.MeshPhongMaterial({
      specular: new THREE.Color(color),
      side: THREE.DoubleSide,
      reflectivity: 0.0,
      color
    })

    const materials = this.viewer.impl.getMaterials()

    materials.addMaterial(
      color.toString(),
      material,
      true)

    return material
  }

  /////////////////////////////////////////////////////////
  // Wraps TextGeometry object and adds a new mesh to
  // the scene
  /////////////////////////////////////////////////////////
  createText (params) {
    
    params.font = new Font(FontJson)
    const geometry = new TextGeometry(params.text,params)

    const material = this.createColorMaterial(
      params.color)

    const text = new THREE.Mesh(
      geometry , material)

    text.position.set(
      params.position.x,
      params.position.y,
      params.position.z)

    this.viewer.impl.scene.add(text)

    this.viewer.impl.sceneUpdated(true)
  }
 
} 

Autodesk.Viewing.theExtensionManager.registerExtension(
  ' ', TextExtension)

Draw the grids

import './Viewing.Extension.Text'

// ...

viewer.loadExtension('Viewing.Extension.Text').then((extension) => { 
    drawGrid()
})

function drawGrid(){

        //get AEC data from the document
         const aecdata = viewer.model.getDocumentNode().getAecModelData()
        //get grids data
         const grids = aecdata.grids

         const linesMaterial = new THREE.LineBasicMaterial({
            color: 0xff0000,
            linewidth: 2
            })
         const circleMaterial = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 
        
        //draw each grid one by one
         grids.forEach(grid => { 
            //label
            const lable = grid.label
            const segments = grid.segments
            //draw each segment one by one
            segments.forEach(seg=>{
               //start point
                const start = seg.points.start
                //end point
                const end = seg.points.end
               
               //grid line
                const lineGeo = new THREE.Geometry () 
                lineGeo.vertices.push (new THREE.Vector3 ( start[0],  start[1],  start[2]))
                lineGeo.vertices.push (new THREE.Vector3 ( end[0],  end[1],  end[2]))
                const line = new THREE.Line (lineGeo,linesMaterial)  
                NOP_VIEWER.impl.scene.add(line) 

                //grid circle
                var circleGeo = new THREE.CircleGeometry( 3, 32 ); 
                var circle = new THREE.Mesh( circleGeo, circleMaterial );
                viewer.impl.scene.add(circle) 
                //transform the circle to the position of max point of bounding box of this grid.
                circle.position.set(grid.boundingBox[3]-1,grid.boundingBox[4],0 );

                //get extension of drawing text
                let textExt= NOP_VIEWER.getExtension('Viewing.Extension.Text') 
                //draw text
                textExt.createText({ 
                    //intensionally to adjust the position of the text
                    //will need to improve to make it more elegant
                    position: {x: grid.boundingBox[3]-1, y: grid.boundingBox[4], z:  0},
                    bevelEnabled: true,
                    curveSegments: 24,
                    bevelThickness: 0.1,
                    color: 0x00ffff,
                    text: grid.label,
                    bevelSize: 0.1,
                    height: 0.01,
                    size: 2
                    })
          })
        })
         viewer.impl.sceneUpdated(true) 
     }

     This produces the result in the screenshot on the top. 

    

 

Related Article