I returned from the Barcelona Forge Accelerator, where I started an exciting new project connecting BIM and the cloud demonstrating two cool possibilities to enhance interaction with the View and Data API:
- A viewer extension enabling interactive model modification, i.e., translation of selected elements, based on Philippe Leefsma's TransformTool
- Real-time communication of the modification back to the source CAD system using:
- A REST API POST call from the viewer extension to the node.js web server
- A direct socket.io connection to broadcast from the web server to any number of desktop clients
Let's look at these topics in more detail:
- Barcelona Forge Accelerator
- Roomedit3d
- It's Read-Only! How can it be Read-Write?
- Genealogy or Where to Start?
- Capturing the TransformTool Selection
- Determining the TransformTool Translation Vector
- POST From Viewer to Server
- Broadcast via Socket.io
- Desktop Notification Connection and Subscription
- Demo Recording
- Download
- To Do
Barcelona Forge Accelerator
Here are some pictures from the accelerator.
Arrival in Barcelona and the Autodesk office in Poble Nou:
Relaxing from intensive cloud programming on a two-hour sailboat cruise:
On Thursday night I found a very nice and friendly restaurant in Poble Nou, Aguaribay. I asked what the word 'aguaribay' means and discovered it is an indigenous South American word for the 'pepper tree' growing aromatic red pepper corn berries. Very tasty.
The accelerator itself went very well.
Here are some notes from the demos on the last day:
- Autoplot Barcelona implemented an AutoCAD I/O REST API app to extract and modify DWG block attributes. After the demo, Philippe suggested making use of the JSONview Chrome extension to pretty-print an intermediate JSON result.
- PTAC EDGE^Revit delivers concrete construction functionality for Revit, information for the job site and production floor. Currently, they ship drawings. Can we deliver this content via an iPAD or tablet and skip the paper? A prototype is up and running now listing content in the cloud.
- Mensch und Maschine worked on two projects: for several FM tasks and for Vault linking with MongoDB. They implemented apps using the View and Data API and AutoCAD I/O.
- Opendesk offers a different approach to designing furniture and making it locally, sustainably and on demand. Currently using Inventor, they explored moving to Fusion and driving CNC machines directly from there.
- Finally, I presented my new roomedit3d sample described below, demonstrating a socket.io broadcast from a web server to any number of connected clients, on the desktop or elsewhere.
All very exciting and promising stuff!
Roomedit3d
Roomedit3d is yet another addition to my collection of samples demonstrating connecting the desktop and the cloud using a Revit BIM model communicating round-trip and real-time with a web-hosted data collection.
This one is based on the same idea as my venerable old RoomEditorApp, with two significant and interesting enhancements:
- Use the View and Data API to present and interact with a full 3D view of the BIM instead of a limited 2D one.
- Use a socket.io connection to communicate changes back to the BIM instead of inefficient REST-based polling.
The RoomEditorApp creates a vastly simplified 2D SVG model of selected rooms, displays it any browser, including on any mobile device, enables you to interactively move and rotate furniture family instances, and updates the BIM model accordingly in real time.
The other important sample connecting the desktop and the cloud is FireRatingCloud.
Just like RoomEditorApp and FireRatingCloud, Roomedit3d consists of two parts:
- roomedit3d, implementing the web server, MongoDB cloud-hosted database and View and Data API interactive 3D model viewer.
- Roomedit3dApp, the C# .NET Revit API add-in client.
Here are the main steps required to implement the roomedit3d node.js web server handling the viewer side of things:
- Set up the View and Data API viewer and display a model, including the REST API to handle the authorisation token.
- Implement an extension to drag a selected element to a new location in the viewer.
- REST API
POST
call to send the element selection and translation data back from the JavaScript viewer browser client to the node.js web server. - Implement a socket.io broadcast to make the modification data available to other subscribing clients.
- Deploy in Heroku.
The C# .NET Revit add-in Roomedit3dApp is still in its early stages, and includes the following:
- A socket.io connection subscribing to the broadcast messages.
- An external event to modify the BIM when updates become available.
- An external app to handle the external event.
- An external command to toggle on and off the broadcast subscription.
It's Read-Only! How can it be Read-Write?
Arif Hanif asked a very valid question in a comment on my initial description of this project:
Question: The roomeditor with View and Data API seems interesting.
I did not know you could make edits in the View and Data API?
Answer: Thank you for your pertinent question.
The View and Data API consists of two parts:
- Translation service from CAD model or other sources to the viewer JSON stream.
- The viewer.
The viewer displays the translated JSON data stream using three.js.
That is completely open source and JavaScript based.
Three.js can also be used to edit the scene.
The system is read-only in the sense that you cannot save back any changes directly back to the original CAD source model.
You can however modify the scene, retrieve and store those modifications in your own format, and implement your own functionality to update the original CAD model accordingly, if you so please.
That is what all my samples connecting BIM and the cloud demonstrate.
I hope this clarifies.
Genealogy or Where to Start?
The first step listed above, setting up the View and Data API viewer and displaying a model, including the REST API to handle the authorisation token, is completely based on Philippe Leefsma's View and Data API boilerplate.
It implements the complete node.js web server, viewer, authorisation token handling and a REST API for the browser client to query the web server for it.
Once that was up and running with the sample model of my choice, I added the TransformTool extension from Philippe's collection of View and Data API JavaScript extensions for the viewer, showing what is doable with the client-side JavaScript API, documented in the discussion on visually moving your components in the viewer.
Those two steps were really quick and easy.
Many thanks to Philippe for his help with them!
Capturing the TransformTool Selection
The TransformTool enables a user to interactively select an element in the viewer and manipulate its location.
We need to communicate the selected element external id and the resulting translation vector back to the desktop CAD model.
I renamed the extension module to Roomedit3dTranslationTool.js
and implemented two variables to capture this data, _externalId
and _initialHitPoint
.
Thee external id is determined from the viewer dbId
in the selection changed event handler like this:
function onSelectionChanged(event) { var dbId = event.dbIdArray[0]; if(dbId) { viewer.getProperties(dbId, function(result){ _externalId = result.externalId; }); } handleSelectionChanged(event.fragIdsArray); }
Determining the TransformTool Translation Vector
The interesting stuff happens in the handleButtonUp
event handler.
It:
- Queries the current tool location
- Calculates the translation vector
offset
- Logs the data to the console
- Packages the data
- Sends it from the JavaScript viewer client in the browser to the node.js web server
this.handleButtonUp = function(event, button) { if( _isDirty && _externalId && _initialHitPoint ) { var offset = subtract_point( _transformControlTx.position, _initialHitPoint ); _initialHitPoint = new THREE.Vector3( _transformControlTx.position.x, _transformControlTx.position.y, _transformControlTx.position.z ); console.log( 'button up: external id ' + _externalId + ' offset by ' + pointString( offset ) ); var data = { externalId : _externalId, offset : offset } options.roomedit3dApi.postTransform(data); _isDirty = false; } _isDragging = false; if (_transformControlTx.onPointerUp(event)) return true; return false; };
POST From Viewer to Server
The postTransform
function invoked above to POST the translation from the viewer to the web server is implemented by the Roomedit3dApiClient
using
the [JavaScript fetch
method(https://fetch.spec.whatwg.org/#dom-global-fetch) defined by]
the Fetch standard and provided by
the whatwg-fetch npm package like this:
var Roomedit3dApiClient = function(args) { var _apiUrl = args.baseUrl; this.postTransform = function(data) { return fetch(_apiUrl + '/transform', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); } }
Broadcast via Socket.io
Now we come to the exciting part.
Once the node.js web server has received the translation information from the viewer client in the browser, it broadcasts it to any number of clients using socket.io.
In case you are interested, here is a nice socket.io getting started sample implementing a bidirectional chat.
The node.js server mainline sets up the socket like this:
var io = require('socket.io'); var roomedit3d = require('./routes/api/roomedit3d'); // . . . app.set('port', process.env.PORT || 3000); var server = app.listen( app.get( 'port' ), function() { var a = server.address().port; console.log( 'Roomedit3d server ' + pkg.version + ' listening at port ' + a + '.' ); var io2 = io(server); io2.on('connection', function(client){ console.log('a client connected to the roomedit3d socket'); }); app.use('/api/roomedit3d', roomedit3d(io2)); } );
The socket is passed in to the roomedit3d
module, which implements the api/roomedit3d/transform
POST route and immediately passes the data on to the socket to broadcast like this:
var express = require('express'); module.exports = function(io) { var router = express.Router(); router.post('/transform', function (req, res) { console.log(req.body); //req.body.externalId; // external id == Revit UniqueId //req.body.offset; // THREE.Vector3 offset x y z io.sockets.emit('transform', req.body); return res.send(); }); return router; }
Pretty easy, isn't it?
Subscribing to the broadcast from the desktop in C# is easier still!
Desktop Notification Connection and Subscription
I started implementing the Roomedit3dApp C# .NET Revit API add-in client.
To test the connection and receive the socket broadcast notifications, however, we have no need for all the Revit API overhead.
I implemented the simple stand-alone console test executable Roomedit3dSocketTest to do so.
It makes use of the SocketIoClientDotNet Socket.IO client library for .NET, which is also available as a NuGet package.
Here is the resulting packages.config:
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="EngineIoClientDotNet" version="0.9.22" targetFramework="net452" /> <package id="Newtonsoft.Json" version="8.0.1" targetFramework="net452" /> <package id="SocketIoClientDotNet" version="0.9.13" targetFramework="net452" /> <package id="WebSocket4Net" version="0.14.1" targetFramework="net452" /> </packages>
With the help of that, the console application mainline implements the following steps:
- Set up the socket.io connection
- Post a message when connected
- On receiving a
transform
message, format and log the info - Loop forever, waiting for messages
That is achieved with the following few lines of code:
using System.Threading; using Newtonsoft.Json.Linq; using Quobject.SocketIoClientDotNet.Client; using System; namespace Roomedit3dSocketTest { class Program { const string _url = "https://roomedit3d.herokuapp.com:443"; static void Main( string[] args ) { var options = new IO.Options() { IgnoreServerCertificateValidation = true, AutoConnect = true, ForceNew = true }; Socket socket = IO.Socket( _url, options ); socket.On( Socket.EVENT_CONNECT, () => { Console.WriteLine( "Connected" ); } ); socket.On( "transform", (data) => { JObject data2 = JObject.FromObject( data ); Console.WriteLine( string.Format( "transform: externalId={0} ({1:0.00},{2:0.00},{3:0.00})", data2["externalId"], data2["offset"]["x"], data2["offset"]["y"], data2["offset"]["z"] ) ); } ); while ( true ) { Thread.Sleep( 100 ); } } } }
Demo Recording
Here is a six-minute demo recording showing:
- View and Data API viewer running locally
- The viewer extension
- The viewer echoing the translation data in the JavaScript debugger console
- The node server logging the POST data received from the viewer when running locally, and forwarding it to the socket.io broadcast
- The same steps running Heroku-hosted in the cloud
- The console app connecting to the cloud and logging the translation messages as they are sent and received
Download
The current versions of roomedit3d and Roomedit3dApp discussed above are release 0.0.4 and release 2017.0.0.2, respectively.
To Do
The Roomedit3dSocketTest console application is just a proof of concept.
Obviously, I now want to implement the real thing:
- Complete the Roomedit3dApp C# .NET Revit API add-in client with its external event, transform message handling, BIM update, etc.
- Besides translation, I would also like the View and Data extension to handle rotation in the XY plane.
- Since this runs so well here, I would like to update the FireRatingCloud sample to use the same technology and implement a socket.io connection between the FireRatingCloud C# .NET Revit add-in and its fireratingdb node.js web and MongoDB server.
Not to mention much more urgent other things, such as preparing for the upcoming Forge DevCon!