Select Theme

Class Summary

Viewing 3D models on a web page or in a mobile App is great, but visualizing design data isn’t just about meshes and textures

In this class I will show you examples of how to use JavaScript to turn the Forge Viewer into a powerful visual reporting tool, giving you access to data associated with each model and connect your application to external databases & Web APIs to add rich visualization experience

Basic Web development experience is preferable - HTML5 JavaScript, CSS, Web Services, REST API’s, …

VISUAL REPORTING

WITH CONNECTED DESIGN DATA

Philippe Leefsma / @F3lipek
Forge Partner Development

AGENDA

  1. Accessing Forge Design Data
  2. UI Customization & Overrides
  3. Connecting Your App to the Cloud

Part I - ACCESSING FORGE DESIGN DATA

Accessing the API



html:
js: function load (urn) { initializationStuff(urn, ..., function(svf) { var viewerContainer = document.getElementById('viewer') var viewer = Autodesk.Viewing.Viewer3D(viewerContainer) viewer.loadModel(getViewablePath(svf)) // call API's viewer.setBackgroundColor( ... ) viewer.loadExtension('MyExtId') // ... }) }

VIEWER COMPONENT MODEL



{
  name: 'chassis'          //display name of the component
  dbId: 53                 //unique id for the component in the model
  fragIds: [38, 39]        //reference the three.js meshes
  parent: 37               //dbId of the parent node
  children: [65, 113, 146] //array of children nodes ids
}

						

MODEL STRUCTURE (1)



function buildModelTree (model) {

  var instanceTree = model.getData().instanceTree

  var rootId = instanceTree.getRootId()

  var rootNode = {
    dbId: rootId,
    name: instanceTree.getNodeName(rootId)
  }

  buildModelTreeRec(rootNode)

  return rootNode
}

						

MODEL STRUCTURE (2)



function buildModelTreeRec (node) {

    instanceTree.enumNodeChildren (node.dbId, function (childId) {

        node.children = node.children || []

        var childNode = {
            dbId: childId,
            name: instanceTree.getNodeName(childId)
        }

        node.children.push(childNode)

        buildModelTreeRec(childNode)
    })
}

						

NODE FRAGMENTS



var instanceTree = model.getData().instanceTree

var fragIds = []

instanceTree.enumNodeFragments(dbId, function (fragId) {

    fragIds.push(fragId)
})

						

MODEL STRUCTURE (3)



function buildModelTreeRec (node) {

  node.fragIds = []

  instanceTree.enumNodeFragments(dbId, function (fragId) {

    node.fragIds.push(fragId)
  })

  instanceTree.enumNodeChildren (node.dbId, function (childId) {

    node.children = node.children || []

    var childNode = {
      dbId: childId,
      name: instanceTree.getNodeName(childId)
    }

    node.children.push(childNode)

    buildModelTreeRec(childNode)
  })
}

						

COMPONENT PROPERTIES



model.getProperties(dbId, function(result) {

    if (result.properties){

        result.properties.forEach( function (prop) {

            console.log(prop)
        })
    }
}
					  


model.getBulkProperties(dbIdArray, ['Material', 'Designer'], function(result) {

    if (result.properties){

        result.properties.forEach( function (prop) {

            console.log(prop)
        })
    }
}
            

Part II - UI CUSTOMIZATION

& OVERRIDES

2D OVERLAYS

Docking Panel


class CustomPanel extends Autodesk.Viewing.UI.DockingPanel {

 constructor(container, title, options = {}) {

  super(container, panelId, title, options)

  this.container.appendChild(...)
 }

}
							

Property Panel


class CustomPropertyPanel extends Autodesk.Viewing.UI.ViewerPropertyPanel {

  constructor(viewer) {

    super(viewer)
  }

  setProperties (properties) {

    properties.push({
      category: 'Web Services'
      displayName:  'Autodesk'
      displayValue: 'Forge'
    })

    super.setProperties(properties)
  }
}
						

FRAGMENT OVERRIDES

Material


// current model
var model = viewer.model

// create custom material
var material = new THREE.MeshPhongMaterial({
    color: '#F43BC1'
    // ... other properties
})

// set material on specific fragId
model.getFragmentList().setMaterial(
    fragId,
    material)

// force viewer to update scene
viewer.impl.invalidate(true)
							

Overlay


// access render proxy
var renderProxy = viewer.impl.getRenderProxy(
    model, fragId)

// clone geometry
var meshProxy = new THREE.Mesh(
    renderProxy.geometry)

meshProxy.matrix.copy(
    renderProxy.matrixWorld)

// create 3d overlay
viewer.impl.addOverlay(
    materialName, meshProxy)

// force update
viewer.impl.invalidate(true)
							

Transform


// access fragment proxy i.e. THREE.Mesh
var fragProxy = viewer.impl.getFragmentProxy(
    model, fragId)

fragProxy.getAnimTransform()

fragProxy.position = new THREE.Vector3(x, y, z)

//Not a standard three.js quaternion
fragProxy.quaternion._x = qx
fragProxy.quaternion._y = qy
fragProxy.quaternion._z = qz
fragProxy.quaternion._w = qw

fragProxy.updateAnimTransform()

viewer.impl.invalidate(true)
							

Dotty Animation Manager

http://trial.dotdotty.com

PLAYING WITH SVG

Scalable Vector Graphics (SVG) is an XML-based markup language for describing two-dimensional vector graphics. SVG is essentially to graphics what HTML is to text.
(source: Mozilla Developer Network)

Coordinates Conversion

2D > 3D


function screenToWorld (screenPoint) {

    var viewport = viewer.navigation.getScreenViewport()

    var n = {
        x: (screenPoint.x - viewport.left) / viewport.width,
        y: (screenPoint.y - viewport.top) / viewport.height
    }

    var worldPoint = viewer.utilities.getHitPoint(n.x, n.y)

    return worldPoint
}
						

3D > 2D


function worldToScreen(worldPoint) {

  var p = new THREE.Vector4()

  p.x = worldPoint.x
  p.y = worldPoint.y
  p.z = worldPoint.z
  p.w = 1

  var camera = viewer.navigation.getCamera()

  p.applyMatrix4(camera.matrixWorldInverse)
  p.applyMatrix4(camera.projectionMatrix)

  var screenPoint = viewer.impl.viewportToClient(p.x, p.y)

  //snap pixel centre
  screenPoint.x = Math.floor(screenPoint.x) + 0.5
  screenPoint.y = Math.floor(screenPoint.y) + 0.5

  return screenPoint
}
						

3D OVERLAYS

Custom Meshes



var geometry = new THREE.SphereGeometry(size, 4, 4)

var mesh = new THREE.Mesh(
  geometry,
  material)

mesh.position.set(x, y, z)

viewer.impl.scene.add(mesh)

						

CSS3DRenderer / CSS3DObject



var cssRenderer = new THREE.CSS3DRenderer()

viewer.container.appendChild(
  cssRenderer.domElement)

var glScene = new THREE.Scene()

var iFrame = document.createElement('iframe')

var cssObj = new THREE.CSS3DObject(iFrame)

cssObj.position.set(x, y, z)

cssObj.scale.set(sx, sy, sz)

glScene.add(cssObj)

						

Part III - CONNECTING YOUR APP TO THE CLOUD

REST / JSON API



var router = express.Router()

router.get('/items/:id', function (req, res) {

  var item = getItemFromDatabase(id)

  res.json(item)
})

var app = express()

app.use('/api', router)

app.listen(process.env.PORT)

						

fetch vs XMLHttpRequest



function getItem(id, onSuccess, onError) {

  fetch('/api/items/' + id).then(function (response) {

    response.json(function (item) {

 	  onSuccess(item)
    })

  }, function (error) {

    onError(error)
  })
}
						

Promises + async / await



async function getItem (id) {

  var response = await fetch('/api/items/' + id)

  var item = await response.json()

  return item
}

async function taskOnItems (itemIds) {

  const itemTasks = itemIds.map((id) => {

    return getItem(id)
  })

  const items = await Promise.all(itemTasks)

  // All items have been retrieved
  items.forEach((item) => {

      console.log('Item: ' + item.name)
  })
}
						

RESOURCES