In forge viewer, for a revit converted file, when making a bubble search:
viewerApp.bubble.search({ 'type': 'geometry', 'role': '3d' });
Or
viewerApp.getSelectedItem()
I get an element node like:
children: (2) [a, a]
data: {guid: "a21582db-704b-df51-dd71-dbf8c12bcc1a", type: "geometry", role: "3d", name: "{3D}", viewableID: "6104055e-60d9-4037-9adc-cd38e10fcfba-00139c8e", …}
id: 8
isLeaf: true
parent: a {parent: a, id: 7, data: {…}, isLeaf: false, children: Array(14)}
I have the guid of the node, and a viewableID.
Then, to display a model, I can call viewerApp.selectItemById(guid/viewableID), which ends displaying the same model.
If I want to point to the 3D view I currently see in the viewer, for future reference (e.g. after revit file update), what is the best attribute for it, guid or viewableID?
Thank you,
The viewable id stands for the unique id of the Revit views in Revit API as my research, but I cannot find the relationship between Revit views and guid in the viewable bubble node. I'm checking with our engineering team if they have some insights.
The viewerApp.selectItemById() is for querying bubble node via its' guid, so you cannot pass viewable id into it. Otherwise, it will return nothing as my investigation.
To archive selecting by viewable id, I would advise you to use the follow instead:
const bubbles = viewerApp.bubble.search({ 'viewableID': '6104055e-60d9-4037-9adc-cd38e10fcfba-00139c8e' });
viewerApp.selectItemById( bubbles[0].guid );
Or extend your own methods (tested with v6.2):
LMV.BubbleNode.prototype.findByViewableId = function (viewableId) {
let item = null;
this.traverse(function (node) {
if (node.data.viewableID === viewableId) {
item = node;
return true;
}
});
return item;
};
LMV.ViewingApplication.prototype.selectItemViewableId = function (viewableId, onItemSelectedCallback, onItemFailedToSelectCallback) {
let item = this.myDocument.getRoot().findByViewableId(viewableId);
if (item) {
return this.selectItem(item, onItemSelectedCallback, onItemFailedToSelectCallback);
}
return false;
};
// -- You codes where you create the ViewingApplication instance
Related
Hello we are using Autodesk forge configurator inventor And we created our own js function. Below, you will find the logic we want to import to the application. On it's own, we make it work, but with forge configurator inventor, we get the authentication error. We tried a lot of different options but failed to make it load the document.
Error is --> GET 401 (Unauthorized)
import repo from '../../Repository';
var options = repo.hasAccessToken() ?
{ accessToken: repo.getAccessToken() } :
{ env: 'Local' };
var documentId = 'urn:MyUrn';
Autodesk.Viewing.Document.load(
documentId, (doc) => {
console.log("test");
let items = doc.getRoot().search(
{
type: "geometry",
role: "3d",
},
true
);
if (items.length === 0) {
console.error("Document contains no viewables.");
return;
}
viewer.loadDocumentNode(doc, items[0], {
keepCurrentModels: true,
//placementTransform: tr,
})
.then(function (model2) {
secondModel = model2;
let tr = secondModel.getPlacementTransform();
let _selecterTr = _selectedModel.getPlacementTransform();
console.log(_selecterTr);
tr = _selecterTr;
secondModel.setPlacementTransform(tr);
viewer.impl.invalidate(true, true, true);
});
}, onDocumentLoadFailure,options);
function onDocumentLoadFailure() {
console.error('Failed fetching Forge manifest');
}
The main issue is that repo does not provide an access token because it was never needed. The models' SVF contents are always loaded directly from the server - instead of relying on the Model Derivative service to provide them.
You just have to provide an endpoint (e.g. /api/viewables/token) on the server-side by adding a Controller that could be called from the client-side in order to get an access token with viewables:read scope.
Detailed information here:
https://forge.autodesk.com/blog/drag-and-drop-design-automation-inventor-sample
I'm trying to load 2 models into Autodesk's Forge Viewer.
I'm trying with the following code:
const urn1 = <urn>
const urn2 = <urn>
Autodesk.Viewing.Initializer(
options,
() => {
const viewerDiv = document.getElementById('MyViewerDiv');
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv);
this.loadDoc(this.props.urns[1], true);
window.setTimeout(e => {
this.loadDoc(this.props.urns[2], false);
}, 4000);
},
);
loadDoc(urn: string, initializeAndLoad: boolean) {
Autodesk.Viewing.Document.load(urn,
(doc) => {
const viewables = Autodesk.Viewing.Document
.getSubItemsWithProperties(doc.getRootItem(), {'type': 'geometry'}, true);
if (viewables.length === 0) {
return;
}
const initialViewable = viewables[0];
const svfUrl = doc.getViewablePath(initialViewable);
const modelOptions = {
globalOffset: {x: 0, y: 0, z: 0}, // to align the models
sharedPropertyDbPath: doc.getPropertyDbPath(),
};
if (initializeAndLoad) {
viewer.start(svfUrl, modelOptions,
() => {},
() => {console.log('load model error');},
);
} else {
viewer.loadModel(urn, modelOptions,
() => {},
(e) => {
console.warn(e);
});
}
},
() => {}
);
}
The rationale behind the timeout is to load the second model using loadModel after the first model has loaded. I've also tried loading the second model from the viewer.start's onSuccess callback.
No matter what, I get the File extension not supported:null ErrorCode:13. error message (both in the console and in a popup)
I'm pretty sure the message is misleading since both urns have valid SVF derivatives (I can switch between them, whichever one is loaded first displays just fine)
NB I'm using the following version:
'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.2/viewer3D.min.js'
As a side note, I've tried using Autodesk.Viewing.ViewingApplication and selectItem. With this I'm able to load multiple models but I don't seem to be able to set modelOptions (specifically globalOffset) with this approach.
The loadModel method expects a URL with some known file extension (e.g., .svf) but you're calling it with an URN (the base64-encoded identifier of a translated document). That's why it's failing to find the file extension.
Btw. if you want to postpone the loading of the second model after the first one is loaded completely, consider using the geometry-loaded-event instead of a timeout.
I have a nested json i eventually broke down to use with ko.mapping:
{ users: [ { k: 'key',
name: 'Alice'
},
],
roles: [ { k: 'key',
name: 'Standard',
regex: [ ( 'root', 'me', 'myself'),
],
},
]
}
...etc.
My situation is that:
my UI needs a data-bind on vm.users.k, but json data (via $.getJSON or $.ajax) is not jet loaded from server;
how can I define the UI, binding a dom element to e.g. vm.users.name, while waiting for ajax request to succeed?
I was looking at this question but maybe I miss the point.
My goal is to define the UI without modeling the initial viewModel, letting it be populated by an Ajax call to server. All nested items and array are needed to became observables.
My last code after some iterations:
function ViewModel( ) {
var self = this;
self.users = ko.observableArray();
self.roles = ko.observableArray();
// etc. logic
};
var viewModel = new ViewModel();
ko.applyBindings( viewModel );
$.getJSON('/api/call/from/server', function( data ){
ko.mapping.fromJS( data.users, {}, viewModel.clienti );
ko.mapping.fromJS( data.roles, {}, viewModel.procedure );
});
And html:
<select name="users" data-bind="options: users,
optionsCaption: '',
optionsText: name,
optionsValue: k">
Server side google app engine with headers:
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
self.response.out.write( json.dumps( object ) )
Actually, I don't want to use async: false.
I managed to broke ko.mapping invocation in three calls from js data.responseJSON, e.g. mapping users to vm.users, then roles to vm.roles, etc.
I'm new to BackboneJS but I'm doing my best to learn it. I'm more familiar with AngularJS so I have some confusion in BackboneJS but would definitely want to become an expert BackboneJS developer too.
Back at my previous job, I was the frontend dev and I would work with the Java dev guy. We would have a meeting about how the JSON response would look like. Basically, I'll make a REST call(either with Restangular or $http) to one of their endpoints and I'll get a response. The JSON response will be assigned to a scope variable such as $scope.bookCollection. In my template, I'll just use ng-repeat to display it.
Now with BackboneJS, I'd like to do it properly. I read today that a BackboneJS Model is a container. What I'd like to happen is that after making a fetch(), I want the JSON response to be put in the Model that I defined. How is that done?
I found an example jsfiddle but I think it's a very bad example. I can't find something that is helpful right now, something with a good fetched data.
require.config({
paths: {
jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
underscore: 'http://underscorejs.org/underscore',
backbone: 'http://backbonejs.org/backbone-min'
},
shim: {
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
underscore: {
exports: "_"
}
}
});
require([
'jquery',
'underscore',
'backbone'], function ($, _, Backbone) {
var UserModel = Backbone.Model.extend({
urlRoot: '/echo/json/',
defaults: {
name: '',
email: ''
}
});
var userDetails = {
name: 'Nelio',
email: 'nelio#angelfire.com'
};
var user = new UserModel(userDetails);
user.fetch({
success: function (user) {
console.log(user.toJSON());
}
});
});
Here is the jsfiddle:
http://jsfiddle.net/20qbco46/
I want the JSON response to be put in the Model that I defined. How is
that done?
If you are trying to render the data from you model, you will use a view for this:
First, create a view to render your data:
// Create a new view class which will render you model
var BookView = Backbone.View.extend({
// Use underscores templating
template: _.template('<strong><%= title %></strong> - <%= author %>'),
initialize: function() {
// Render the view on initialization
this.render();
// Update the view when the model is changed
this.listenTo(this.model, "change", this.render);
},
render: function() {
// Render your model data using your template
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
See also: template and toJSON as well as $el
Next, create a Model:
// Create a model class
var Book = Backbone.Model.extend({
urlRoot: '/echo/json/',
defaults: {
title : '',
author: ''
},
});
Your model will hold the data fetched from the url / urlRoot
You can use set if you are trying to add new attributes to your model.
You can use get to grab attributes from your model.
See also - save and destroy.
Then, instantiate your model:
// Some dummy data
var instance = {
title: 'learn Backbone JS',
author: 'Bobby Longsocks',
};
// Instansite your model
var model = new Book(instance);
And finally, fetch your model data and create a new instance of you view:
// Fetch your model
model.fetch({
success: function(book) {
// Instansite your view, passing in your model
var view = new BookView({model: book, el: $('body')});
}
});
Here is an Example you can fiddle with.
And some further reading: Annotated Source
I want to load a tree structure in dojo dynamically with every layer loading(fetching data from server) only after a click to the next layer is made. This would help me to not have the entire tree loaded in memory all together but as someone clicks a level of a tree, only then all the elements of the next level are fetched from the server using ajax requests in dojo.
Can someone help me on how to go about this?
The answer for this is here as follows: using a dijit tree, it has just 3 steps to create a dynamic tree that loads lazily: 1. Create a store, 2. Create a model, 3. Create the tree. Here is the code snippet that I used which worked perfectly:
function createLazyTreeStore()
{
var store = new JsonRest({
target: "location where your file is",
getChildren: function(object) {
// object may just be stub object, so get the full object first and then return it's
// list of children
return this.get(object.id).then(function(fullObject){
console.log(fullObject.children);
return fullObject.children;
});
}
});
return store;
}
function createModel(store)
{
var model = new ObjectStoreModel({
store: store,
getRoot: function(onItem) {
this.store.get("the .php file").then(function(item)
{
onItem(item[0]);
});
},
getChildren: function(object, onComplete, onError)
{
this.store.get("your url to fetch the data with parent child relation").then(function(fullObject) {
object.children = fullObject;
onComplete(object.children);
}, function(error)
{
console.error(error);
onComplete([]);
});
},
mayHaveChildren: function(object) {
return true;
}
});
return model;
}
function createTree(model)
{
var tree = new Tree({
model: model,
style: "your preference",
id: "tree",
});
return tree;
}
Then call these functions in the following order:
var store = createLazyTreeStore();
var model = createModel(store);
var tree = createTree(model);
accordionPane.addChild(tree);