Post on 22-Jul-2020
ConnectinganyDesktopApplicationorRevitAdd-inwithAutodeskForgeandtheCloud
JeremyTammikAutodeskInc.
http://thebuildingcoder.typepad.com/blog/2016/10/connecting-desktop-and-cloud-at-rtc-material.html
§ Free your BIM data – address all BIM participants!§ Why connect? What cloud? How about security?§ It's easy, and almost everything open source§ The 2D SVG cloud-based round-trip room editor§ NoSQL databases, CAP theorem and ACID versus BASE§ FireRating in the Cloud§ Forge overview§ Forge-based round-trip BIM editor
§ Roomedit3dv3
Session Summary
Participant counts grow by orders of magnitudeBuilding design, construction, maintenance, use
§ design - architect, engineer - Revit§ visualise - client, everybody - Viewer§ collaborate - management – Glue, Plan§ make - construction – Field, Layout§ use - inhabit, maintain, FM
BIM Collaboration Roles
§ Revit and BIM § Shrinking numbers of desktop computers
§ A growing number of participants§ Growing numbers of mobile devices
§ Glue code, connected custom components§ Internet, cloud§ HTML5, SVG, WebGL§ Forge
Trends, Tools and Technologies
§ Local and totally private§ 2D room editor uses private CouchDB web server
§ Global with Internet security§ FireRatingCloud uses node.js web server on
Heroku and MongoDB database on mongolab§ Forge OAuth
§ Roomedit3dv3 uses Forge OAuth
What Cloud? Private? Secure?
§ Simplify your data§ Graphics? 2D? 3D? Properties? § Customise, optimal workflow, minimal complexity§ Based on 'need to know'
§ Use existing components§ Minimise add-ins, custom components, glue code§ Open source§ Forge
Keep It Simple!
§ Perfection§ Perfection is achieved, not when there is nothing more to add, but
when there is nothing left to take away – Antoine de Saint-Exupéry
§ Lazy§ ... develop the three great virtues of a programmer: laziness,
impatience, and hubris – Larry Wall
§ Simple§ Simplicity is the ultimate sophistication – Leonardo da Vinci§ There is no greatness where there is no simplicity – Leo Tolstoy
§ KISS
Quotes on Three Fundamental Aspects
§ Simplified 2D BIM room editor, SVG graphics§ FireRating in the Cloud, the simplest sample§ Roomedit3d, Forge based
Sample Overview
§ I am not suggesting modelling in the web§ If you want to do so, talk with Autodesk
§ Revit I/O§ http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.28b
Caveat
2D Room Editor
§ “Not only SQL”§ Next generation database paradigm
§ Some characteristics§ Non-relational, distributed, open-source, scalable, huge data
§ Frequent other characteristics§ Schema-free, easy replication support, simple API,
eventually consistent, i.e., BASE, not ACID
http://nosql-database.orghttps://en.wikipedia.org/wiki/NoSQLhttp://www.mongodb.com/nosql-explained
NoSQL
§ ACID§ Atomicity, Consistency, Isolation and Durability guarantee that
database transactions are processed reliably
§ CAP Theorem§ The ACID paradigm cannot simultaneously guarantee consistency,
availability and partition tolerance (distributed system)
§ BASE§ Basic Availability, Soft-state, Eventual consistency
http://thebuildingcoder.typepad.com/blog/2014/05/views-displaying-given-element-svg-and-nosql.html#5
CAP and ACID versus BASE
§ BIM – Building Information Model§ Cloud-based data repository§ 2D rendering on mobile device
Data Source, Repository and Consumer Client
§ Graphical room editor on mobile device
§ Update cloud database§ Reflect real-time changes in BIM
Real-time Editing Triggers Database and BIM Update
§ Simple navigation§ Home page: list all models, select
one§ Model selected: list all levels,
select one§ Level selected: list all rooms,
select one§ Room selected: display 2D
graphical editor, click and drag furniture
§ Furniture selected: display and
Sixty Second First Impression Demo
Free and Simple
§ 100% open source § 200 hours research§ < 8 hours to rebuild
§ Install components§ Re-implement from
scratch§ Two implementation files
§ index.html (474 lines)§ roomedit.js (358 lines)
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>
<h1>Room Editor</h1>
<style>table{border-collapse: collapse;
}
table, th, td{border: 0px solid black;padding: 5px;
}
.pointer{cursor: pointer;
}
button:hover{cursor: pointer;
}
.disable {opacity : .35;background-color:lightgray;
}</style>
<div id="content"></div>
<ul id="navigatorlist"></ul>
<div id="editor"></div>
<p id="current_furniture"></p>
<script type="text/javascript" src="modules.js"></script><script type="text/javascript" src="raphael-min-jt.js"></script><script type="text/javascript" src="roomedit.js"></script><script type="text/javascript">
var three_spaces = ' \u00a0 '; // unicode
var db = require('db').current();var $ = require('jquery');
var furniture;var current_furniture = null;var current_furniture_doc = null;var all_furniture = [];
var xmin;var ymin;var xscale;var yscale;var tooltip;var tooltip_bg;var paper;
// convert URL parameters to dictionary
var url = this.location;
var paramdict = {};var params = url.href.split("?");
if( 1 < params.length ) {url = params[0];paramdict = get_url_paramdict( params[1] );
}
if (paramdict.hasOwnProperty('furnitureid')){
//======================================================// display and edit the selected element properties//======================================================
var fid = paramdict['furnitureid'];db.getDoc( fid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}current_furniture_doc = resp;var rid = current_furniture_doc.roomId;var a = current_furniture_doc.properties;
var p = $('<p/>').appendTo('#content');p.append( $('<a/>').text('Home').attr('href',url) );p.append( document.createTextNode( three_spaces ) );p.append( $('<a/>').text('Back').attr('href', url + '?roomid=' + rid ) );
p = p.append($('<p/>').text('Element properties of '+ current_furniture_doc.name + ':' ));
var keys = jt_get_keys( a );keys.sort();
var table = p.append($('<table/>'));var n = keys.length;for( var i=0; i<n; ++i ) {var key = keys[i];var val = a[key];var key_label = capitalise(key);var key_id = unspace(key);var readonly = ('r' == val[0]);v = val.slice(2);table.append( '<tr><td>' + key_label+ ':</td><td><input type="text" id="' + key_id+ '" value="' + v + '"'+ (readonly ? ' readonly class="disable"' : '')+ '></td></tr>' );
.createTextNode( three_spaces ) );
table.append($('<input/>').attr( 'type', 'button').attr( 'value', 'Cancel').attr( 'onclick',
'save_properties(current_furniture_doc,false)'));}
);}else if (paramdict.hasOwnProperty('roomid')){
//======================================================// display the selected room and its furniture//======================================================
var rid = paramdict['roomid'];db.getDoc( rid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var roomdoc = resp;var lid = roomdoc.levelId;db.getDoc( lid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var leveldoc = resp;var mid = leveldoc.modelId;db.getDoc( mid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var modeldoc = resp;var q = { key: JSON.stringify(rid) };db.getView('roomedit',
'map_room_to_furniture', q,function (err, data) {if (err) {alert(JSON.stringify(err));
}var furniture=[];var symbol_ids = [];var n = data.rows.length;for (var i = 0; i < n; ++i) {var instdoc = data.rows[i].value;var fid = instdoc._id;console.log( 'doc id ' + fid + ' ' +
instdoc.name );if( instdoc.roomId != rid ) {alert( 'room ids differ in furniture
' + instdoc._id+ ':\ndoc ' + instdoc.roomId +
"\nurl " + rid );}var sid = instdoc.symbolId;furniture.push(instdoc);symbol_ids.push( sid );
}var q2 = { keys:
JSON.stringify(symbol_ids) };db.getView('roomedit', 'symbols', q2,function (err, data) {if (err) {alert(JSON.stringify(err));
}var n2 = data.rows.length;var map_symbid_to_loop = {};for( var i = 0; i < n2; ++i ) {var symbdoc = data.rows[i].value;map_symbid_to_loop[symbdoc._id] =
symbdoc.loop;}for( var i = 0; i <
furniture.length; ++i ) {furniture[i].loop =
map_symbid_to_loop[furniture[i].symbolId];}var url_model = url + '?modelid=' +
mid;var url_level = url + '?levelid=' +
lid;var url_room = url + '?roomid=' +
rid;
$('#content').append( $('<table/>').append( $('<tr>').append( $('<td>').text(
'Start:' ) ).append( $('<td>').append( $('<a>').text('Home').attr('href',url) )))
.append( $('<tr>').append( $('<td>').text(
'Model:' ) ).append( $('<td>').append( $('<a>').text(modeldoc.name).attr('href',url_model) )))
.append( $('<tr>').append( $('<td>').text(
'Level:' ) ).append( $('<td>').append( $('<a>').text(leveldoc.name).attr('href',url_level) )))
.append( $('<tr>').append( $('<td>').text( 'Room:'
) ).append( $('<td>').append( $('<a>').text(roomdoc.name).attr('href',url_room) ))))
.append($('<p/>').text( n.toString()+ ' furniture and equipment
item' + pluralSuffix( n )+ ' in room ' )
.append( $('<i/>').text(
document.createTextNode( '.' ) ) ).append($('<p/>').text( 'Please pick and drag
furniture '+ 'and equipment around to
select and move it, '+ 'then click the buttons to
rotate clockwise, '+ 'counter-clockwise, refresh,
and save.' ));
var p = $('<p/>').appendTo('#content');
p.append( $('<button/>').text('Properties')
.attr('onclick', 'edit_properties_current(url)' ) );
p.append( document.createTextNode( three_spaces ) );
p.append( $('<button/>').text('Rotate')
.attr('onclick', 'rotate_current_cw()' ) );
p.append( document.createTextNode( three_spaces ) );
p.append( $('<button/>').text('Ccw').attr('onclick',
'rotate_current_ccw()' ) );p.append( document.createTextNode(
three_spaces ) );p.append(
$('<button/>').text('Refresh').attr('onclick', 'refresh()' ) );
p.append( document.createTextNode( three_spaces ) );
p.append( $('<button/>').text('Save')
.attr('id', 'save')
.attr('onclick', 'save_all()' ) );
raphael( roomdoc, furniture );}
);}
);}
);}
);}
);}else if( paramdict.hasOwnProperty( 'levelid' ) ) {
//======================================================// display a menu of all rooms on selected level//======================================================
var lid = paramdict['levelid'];db.getDoc( lid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var leveldoc = resp;var mid = leveldoc.modelId;db.getDoc( mid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var modeldoc = resp;db.getView('roomedit','map_level_to_room',{ key: JSON.stringify(lid) },
function (err, data) {if (err) {alert(JSON.stringify(err));
}
var url_model = url + '?modelid=' + mid;var url_level = url + '?levelid=' + lid;
$('#content').append( $('<table/>').append( $('<tr>').append( $('<td>').text( 'Start:' ) ).append( $('<td>').append( $('<a>').text('Home').attr('href',url) )))
.append( $('<tr>').append( $('<td>').text( 'Model:' ) ).append( $('<td>').append( $('<a>').text(modeldoc.name).attr('href',url_model) )))
.append( $('<tr>').append( $('<td>').text( 'Level:' ) ).append( $('<td>').append( $('<a>').text(leveldoc.name).attr('href',url_level) ))));
var n = data.rows.length;
var p = $('<p/>').text( n.toString()+ ' room' + pluralSuffix( n )+ ' on level ' ).appendTo('#content');
p.append( $('<i/>').text( leveldoc.name ) );p.append( document.createTextNode( ' in
model ' ) );p.append( $('<i/>').text( modeldoc.name ) );p.append( document.createTextNode( '.' ) );p.append( $('<p/>').text( 'Please select
one:' ));
for (var i = 0; i < n; ++i) {var doc = data.rows[i].value;if( doc.levelId != lid ) {alert( 'model ids differ: doc ' +
doc.levelId+ ", url " + lid );
});
});
}else if ( paramdict.hasOwnProperty( 'modelid' ) ) {
//===========================================================
// display a menu of all levels and sheets in selected model
//===========================================================
var mid = paramdict['modelid'];db.getDoc( mid,function(err,resp) {if (err) {alert(JSON.stringify(err));
}var modeldoc = resp;db.getView('roomedit','map_model_to_level_and_sheet',{ key: JSON.stringify(mid) },
function (err, data) {if (err) {alert(JSON.stringify(err));
}var url_model = url + '?modelid=' + mid;
$('#content').append( $('<table/>').append( $('<tr>').append( $('<td>').text( 'Start:' ) ).append( $('<td>').append( $('<a>').text('Home').attr('href',url) )))
.append( $('<tr>').append( $('<td>').text( 'Model:' ) ).append( $('<td>').append( $('<a>').text(modeldoc.name).attr('href',url_model) ))));
var n = data.rows.length;var nLevel = 0;var nSheet = 0;for (var i = 0; i < n; ++i) {var doc = data.rows[i].value;if( doc.modelId != mid ) {alert( 'model ids differ: doc '+ doc.modelId + ", url " + mid );
}var t = doc.type; // level or sheetvar s = url;
if( 'level' == t ) {++nLevel;
}else {++nSheet;s = s.replace( 'index.html', 'index2.html'
);}
s += '?' + t + 'id=' + doc._id;
t = t.substr(0,1).toUpperCase() + t.substr(1);
$('<li/>').append(document.createTextNode(t + ': '))
.append($('<a>').attr('href',s).text(doc.name)).appendTo('#navigatorlist');
}var prompt =nLevel.toString() + ' level' + pluralSuffix(
nLevel ) + ' and ' +nSheet.toString() + ' sheet' + pluralSuffix(
nSheet ) + ' in model ';
$('#content').append($('<p/>').text( prompt ).append( $('<i/>').text( modeldoc.name ) ).append( document.createTextNode( '.' ) ) )
.append($('<p/>').text( 'Please select a level or sheet:' )
);}
);}
);}else {
//======================================================// display a menu of all available models//======================================================
db.getView('roomedit', 'models',function (err, data) {if (err) {alert(JSON.stringify(err));
}var n = data.rows.length;
$('#content').append($('<p/>').text( n.toString() + ' model' + pluralSuffix( n
)+ ' available. Please select one:' ));
var list = $('#navigatorlist');
for (var i = 0; i < n; ++i) {var doc = data.rows[i].key;var s = url + '?modelid=' + doc._id;list.append($('<li/>')
§ Index.html§ Roomedit.js§ One day to extract data from Revit§ One day to enhance database, editor, update
etc.
Update Splits Functionality
§ BIM – Revit§ Data repository – NoSQL§ Rendering and editing – HTML and
SVG
Base Technologies
§ Revit BIM – Revit .NET add-in§ NoSQL Database – Apache CouchDB§ HTML, SVG – JavaScript, jquery, db,
Raphaël
Implementation Environment
§ Same origin policy§ Server-side scripting§ Two components instead of three
Simpler Still
js
§ Revit add-in§ CouchDB database
The Two and Only Projects
§ Everything is a document§ All documents are JSON§ Every document has built-in id and revision§ The database design is also a document§ The design defines views and attachments
CouchDB Database Implementation
§ Relax!§ All interactions are REST§ Management console is Futon
http://127.0.0.1:5984/_utils/
§ Access all documentshttp://127.0.0.1:5984/roomedit/_all_docs
§ Include full doc data, not just id and revisionhttp://127.0.0.1:5984/roomedit/_all_docs?include_docs=true
CouchDB Database Interaction
§ Model – a RVT project file§ Level§ Room§ FamilyInstance – furniture or equipment§ FamilySymbol – geometry
BIM Model
§ Room has boundary loops and can contain holes§ FamilySymbol has a single boundary loop§ FamilyInstance has a 2D placement
§ Translation§ Rotation
BIM Object Graphics
§ CouchDB ids are Revit unique ids§ Family instance → room → level → model § Family instance → symbol
BIM Object Relationships
§ DbObj base class§ DbModel§ DbLevel§ DbRoom§ DbFurniture§ DbSymbol
NoSQL Database Structure
§ DbFurniture.symbolId → DbSymbol§ DbFurniture.roomId → DbRoom§ DbRoom.levelId → DbLevel§ DbLevel.modelId → DbModel
Database Object Relationships
§ All graphics represented by SVG path element data
<svg width="4cm" height="4cm" viewBox="0 0 400 400"xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect x="1" y="1" width="398" height="398"fill="none" stroke="blue" />
<path d="M 100 100 L 300 100 L 200 300 z"fill="red" stroke="blue" stroke-width="3" />
</svg>
Database Object Graphics and Placement
§ Family symbol§ Define geometry
{"_id": "11cc6e52-519e-49b2-9813-c9561b59a1fd-0005f5fc","_rev": "1-d575ca095533db4ccbed9f7ab2607a12","loop": "M-191 922 L190 922 216 862 190 859 -191 859 -216 862 -216 919Z","type": "symbol","description": "FamilySymbol Furniture <390652 Table ronde a chaises>","name": "Table ronde avec chaises - 01"
}
JSON Symbol Database Document
§ Furniture doc represents family instance and defines§ Relationship to room and family symbol§ Placement = transform = translation + rotation
{"_id": "11cc6e52-519e-49b2-9813-c9561b59a1fd-0005f65b","_rev": "1-c1b4fc969181267b55dab4c6857fc5d7","roomId": "cbe571b0-0593-4350-a8e6-abf3c9239325-00061210","symbolId": "11cc6e52-519e-49b2-9813-c9561b59a1fd-0005fe6d","transform": "R-90T-10429,1020","type": "furniture","description": "FamilyInstance Furniture <390747 Canapé..."name": "Canapé 3 places"
}
JSON Instance Database Document
§ A view defines a map and optional reduce function§ The map produces key-value pairs§ Reduce produces an accumulation
exports.models = {map: function (doc) {if( 'model' == doc.type ) {emit(doc, null);
}}, reduce: function (key, values, rereduce) {return sum(values);
}}
CouchDB Views
RoomEditorViews• models• levels• rooms• furniture• symbols• map_room_to_furniture• map_level_to_room• map_model_to_level
rooms = {map: function (doc) {if( 'room' == doc.type ) {emit(doc, null);
}}
};
map_level_to_room = {map: function (doc) {if( 'room' == doc.type ) {emit(doc.levelId, doc);
}}
};
§ Specific document
§ http://127.0.0.1:5984/roomedit/11cc6e52-519e-49b2-9813-c9561b59a1fd-0005f5fc
§ Specific view
§ http://127.0.0.1:5984/roomedit/_design/roomedit/_view/models
§ Specific key in view
RESTful CouchDB Document and View Access URLs
Entire CouchDB Application Definition
§ Kanso loads CouchDBroomedit/data/room_model_9.jsonroomedit/index.htmlroomedit/kanso.jsonroomedit/lib/app.jsroomedit/lib/views.jsroomedit/raphael-min-jt.js
roomedit/roomedit.js
http://kan.so
§ kanso.json{
"name": "roomedit","attachments": ["index.html",
"raphael-min-jt.js"],"modules": ["lib"],"load": "lib/app","dependencies": {
"attachments": null,"modules": null,"properties": null,"db": null,"jquery": null
}}
§ HTML scaffolding§ JavaScript query section§ Raphaël SVG generation and interaction§ RESTful database updates§ Called with
§ No argument: list all models§ Model id: list all levels in model§ Level id: list all rooms on level§ Room id: display graphical editor and enable
Index.html
<h1>Room Editor</h1>
<div id="content"></div>
<ul id="navigatorlist"></ul>
<div id="editor"></div>
<p id="current_furniture"></p>
<script type="text/javascript" src="modules.js"></script><script type="text/javascript" src="raphael-min-jt.js"></script>
§ Populated entirely using JavaScript adding HTML and SVG nodes using jquery, raphael and db for CouchDB queries
Minimal Predefined HTML Scaffolding
List all Models
§ No input parameter§ View ‘models’, no key§ Populate navigator list
db.getView('roomedit', 'models',function (err, data) {
if (err) {return alert(err);
}var n = data.rows.length;for (var i = 0; i < n; ++i) {
var doc = data.rows[i].key;var s = url + '?modelid=' + doc._id;$('<li/>').append($('<a>')
.attr('href',s)
.text(doc.name)).appendTo('#navigatorlist');
}var p = $('<p/>').appendTo('#content');p.append( $('<a/>').text('Home').attr('href',url) );p.append( document.createTextNode( three_spaces ) );p.append( $('<a/>').text('Back').attr('href',url) );
$('<p/>').text( 'Please select a model in the list below.' ).appendTo('#content');
$('<p/>').text(n.toString() + ' model’ + pluralSuffix( n ) + dotOrColon( n ) ).appendTo('#content');
});
§ Input parameter ‘modelId’§ Get model document itself
§ Display current selection§ View ‘map_model_to_level’ with model id key
§ Populate navigator list
List all Levels in Selected Model
§ Input parameter ‘levelId’§ Get level document itself → model id§ Get model document
§ Display current selection§ View ‘map_level_to_room’ with level id key
§ Populate navigator list
List all Rooms on Selected Level
§ Input parameter ‘roomId’§ Get room document itself → level id§ Get level document → model id§ Get model document
§ Display current selection§ View ‘map_room_to_furniture’ with key room
id§ Retrieve family instances → symbol ids§ View ‘symbols’ with list of symbol keys
Display and Edit a Selected Room
§ Nested asynchronous database queries§ Each nested query depends on previous
results§ Page cannot be displayed until all complete§ Nested callback functions
Nested Database Queries and Callback Functions
§ Using jquery and Raphaël§ Input room + furniture populated with symbol
SVG path§ Determine size and aspect ratio§ Instantiate paper = canvas§ Place room and attach tooltip events
mouseover + out§ Place furniture and attach identification, drag
and tooltip events
SVG Room Editor
§ Determine 2D boundary polygon loops
§ Upload model to database§ Download changes from database§ Subscribe to real-time updates§ Timer and external event
Revit Add-in
§ How does the Revit add-in access CouchDB?§ Upload model data§ Retrieve database changes§ Subscribe to changes
§ DreamSeathttps://github.com/vdaron/DreamSeat
§ Easy!§ Good job!
Revit Add-in Interaction with CouchDB
§ Create the shared 'Fire Rating' parameter§ Export fire rating values for all doors§ Import the modified values back into BIM§ Store data for multiple projects
§ Cloud database, Revit UniqueId§ Subscribe to changes
FireRating in the Cloud Commands
FireRating in the Cloud Architecture
§ Revit add-in§ Stand-alone
FireRating in the Cloud C# REST Client
§ /a/src/web/mongo/firerating/server.js
FireRating in the Cloud Node.js MongoDB Server
https://mlab.comhttps://mlab.com/databases/firerating
https://mlab.com/databases/firerating/collections/doors ?q=%7B%22tag%22%3A%22jeremy%22%7D
FireRating in the Cloud Mongolab Database
§ Forge is a platform to empower developers§ They can in turn empower their users
§ design§ visualise§ collaborate§ make§ use
Forge Platform Empowers Developers
Forge Components
Forge Based BIM Editor
§ Roomedit3d pre-DevCon – hard-coded§ Roomedit3dv2 for DevCon – A360, deprecated§ Roomedit3dv3 – boilerplate + a dozen lines
§ https://roomedit3dv3.herokuapp.com
Roomedit3dv3 Evolution
§ Realistic model rendering in both 2D and 3D, optionally linked§ Complete access to all BIM data, geometry, structure, properties§ Not bound to any specific model§ Secure authenticated access§ Embedded in a full ecosystem of mature CAD related web services§ Minimal amount of coding based on boilerplate sample code
Advantages of a Forge Based App
§ 2D RoomEditorApp and roomeditdbhttps://github.com/jeremytammik/RoomEditorApphttps://github.com/jeremytammik/roomedit
§ Properties FireRatingCloud and fireratingdbhttps://github.com/jeremytammik/FireRatingCloudhttps://github.com/jeremytammik/firerating
§ Forge Roomedit3dApp, roomedit3d and roomedit3dv3https://github.com/jeremytammik/Roomedit3dApphttps://github.com/jeremytammik/roomedit3dhttps://github.com/Autodesk-Forge/
forge- boilers.nodejs/tree/roomedit3d
Sample Repositories
JeremyTammikAutodeskInc.
Questions?Session1.4– ConnectingDesktopandCloud
jeremy.tammik@autodesk.com– @jeremytammik