How to get location with dbid in 2D view - javascript

There is a simular question:
How to get location with dbid in 2D
but hasn't been anwsered.
So I have the same question, is there a way to get the object location info using dbid in 2D view?
We have a requirement in our product to mark certain object(s) and save the dbids in external database, and when next time opening the model, we need to use those ids and find the locations and then draw some custom shape to highlight those objects.
I tried to use viewer.impl.highlightObjectNode, but it can only display the object as it is selected, it's very limited on custom vitualization.

Here are some code snippets to access mesh information of Forge fragments, it might help you to find the location of a certain Forge viewer dbId. For more information, you can refer this extentsion: https://github.com/Autodesk-Forge/library-javascript-viewer-extensions/blob/master/src/Autodesk.ADN.Viewing.Extension.MeshData/Autodesk.ADN.Viewing.Extension.MeshData.js
Besides, viewer.impl.highlightObjectNode for highlighting elements only, it cannot be used in other purposes as my experince.
function getLeafFragIds( model, leafId ) {
const instanceTree = model.getData().instanceTree;
const fragIds = [];
instanceTree.enumNodeFragments( leafId, function( fragId ) {
fragIds.push( fragId );
});
return fragIds;
}
function getComponentGeometry( viewer, dbId ) {
const fragIds = getLeafFragIds( viewer.model, dbId );
let matrixWorld = null;
const meshes = fragIds.map( function( fragId ) {
const renderProxy = viewer.impl.getRenderProxy( viewer.model, fragId );
const geometry = renderProxy.geometry;
const attributes = geometry.attributes;
const positions = geometry.vb ? geometry.vb : attributes.position.array;
const indices = attributes.index.array || geometry.ib;
const stride = geometry.vb ? geometry.vbstride : 3;
const offsets = geometry.offsets;
matrixWorld = matrixWorld || renderProxy.matrixWorld.elements;
return {
positions,
indices,
offsets,
stride
};
});
return {
matrixWorld,
meshes
};
}
var meshInfo = getComponentGeometry( viewer, 1234 );

you can also check "viewer.impl.fitToView" in viewer3D.js, this function calculates selected objects union bounding box and fit them to view, so I extract code from this function to get object bounding box center coordinates with it's dbid
function getObjectBound2D(viewer, objectId) {
var model = viewer.model;
// This doesn't guarantee that an object tree will be created but it will be pretty likely
var bounds, bc, i;
if (model.is2d()) {
bounds = new THREE.Box3();
// move this next one up into the calling method
bc = new avp.BoundsCallback(bounds);
var dbId2fragId = model.getData().fragments.dbId2fragId;
var fragIds = dbId2fragId[objectId];
// fragId is either a single vertex buffer or an array of vertex buffers
if (Array.isArray(fragIds)) {
for (var j = 0; j < fragIds.length; j++) {
// go through each vertex buffer, looking for the object id
find2DBounds(model, fragIds[j], objectId, bc);
}
} else if (typeof fragIds === 'number') {
// go through the specific vertex buffer, looking for the object id
find2DBounds(model, fragIds, objectId, bc);
}
// should have some real box at this point; check
if (!bounds.empty()) {
return bounds;
}
}
function find2DBounds(model, fragId, dbId, bc) {
var mesh = model.getFragmentList().getVizmesh(fragId);
var vbr = new avp.VertexBufferReader(mesh.geometry);
vbr.enumGeomsForObject(dbId, bc);
}
function find2DLayerBounds(model, fragId, bc) {
var mesh = model.getFragmentList().getVizmesh(fragId);
var vbr = new avp.VertexBufferReader(mesh.geometry);
var visibleLayerIds = that.getVisibleLayerIds();
vbr.enumGeomsForVisibleLayer(visibleLayerIds, bc);
}
};
var objBoundingbox = getObjectBound2D(viewer,dbid);
var objCenterCoordinates = objBoundingbox.center();

Related

My basic question is how do I access the properties or methods of an individual object in a list of objects?

I am creating an interactive Maths quiz with 9 triangles generated as objects. The user selects a triangle from a drop-down list and its image and dimensions appear and a variable (thisOne) is set as the number of the triangle (1 to 9).
The user inputs their answer (the area of the triangle).
I would have thought that I could generate the correct answer with something like this
correctAnswer = ("triangle_"+ thisOne).triArea()
but it doesn't work.
My basic question is how do I access the properties or methods of an individual object in a list of objects?
Any help appreciated
<!-- ---------------- Triangle object ----------------- -->
function Triangle(triSrc, triName, triBase, triHeight, triUnits){
this.triSrc = triSrc;
this.triName = triName;
this.triBase = triBase;
this.triHeight = triHeight;
this.triArea = function(){
return triBase * triHeight /2
};
this.triUnits = triUnits;
}
var triangle_1 = new Triangle('tri_q_area_1','right angle triangle',34,20,'m2');
var triangle_2 = new Triangle('img/tri_q_area_2.png','right angle triangle',16,16,'cm2');
var triangle_3 = new Triangle('img/tri_q_area_3.png','right angle triangle',15,8,'km2');
var triangle_4 = new Triangle('img/tri_q_area_4.png','right angle triangle',15,9,'cm2');
var triangle_5 = new Triangle('img/tri_q_area_5.png','scalene', 20,12,'cm2');
var triangle_6 = new Triangle('img/tri_q_area_6.png','scalene', 200,130,'cm2');
var triangle_7 = new Triangle('img/tri_q_area_7.png','scalene', 20,16,'m2');
var triangle_8 = new Triangle('img/tri_q_area_8.png','scalene', 28,16,'km2');
var triangle_9 = new Triangle('img/tri_q_area_9.png','scalene', 54,38,'yards2');
are you ok with have a list?
<!-- ---------------- Triangle object ----------------- -->
function Triangle(triSrc, triName, triBase, triHeight, triUnits){
this.triSrc = triSrc;
this.triName = triName;
this.triBase = triBase;
this.triHeight = triHeight;
this.triArea = function(){
return triBase * triHeight /2
};
this.triUnits = triUnits;
}
var triangles [
new Triangle('tri_q_area_1','right angle triangle',34,20,'m2'),
new Triangle('img/tri_q_area_2.png','right angle triangle',16,16,'cm2'),
new Triangle('img/tri_q_area_3.png','right angle triangle',15,8,'km2'),
new Triangle('img/tri_q_area_4.png','right angle triangle',15,9,'cm2'),
new Triangle('img/tri_q_area_5.png','scalene', 20,12,'cm2'),
new Triangle('img/tri_q_area_6.png','scalene', 200,130,'cm2'),
new Triangle('img/tri_q_area_7.png','scalene', 20,16,'m2'),
new Triangle('img/tri_q_area_8.png','scalene', 28,16,'km2'),
new Triangle('img/tri_q_area_9.png','scalene', 54,38,'yards2')
];
and then do triangles[thisOne].triArea()
You should use an array for this sort of thing. Here's an example:
<!-- ---------------- Triangle object ----------------- -->
function Triangle(triSrc, triName, triBase, triHeight, triUnits){
this.triSrc = triSrc;
this.triName = triName;
this.triBase = triBase;
this.triHeight = triHeight;
this.triArea = function(){
return triBase * triHeight /2
};
this.triUnits = triUnits;
}
var triangle = []; // declare the array;
// note that i'm skipping index 0 to keep with your sample, that is not normally a smart thing to do!
// assign to indices of the array, no need to declare individual elements.
triangle[1] = new Triangle('tri_q_area_1','right angle triangle',34,20,'m2');
triangle[2] = new Triangle('img/tri_q_area_2.png','right angle triangle',16,16,'cm2');
triangle[3] = new Triangle('img/tri_q_area_3.png','right angle triangle',15,8,'km2');
triangle[4] = new Triangle('img/tri_q_area_4.png','right angle triangle',15,9,'cm2');
triangle[5] = new Triangle('img/tri_q_area_5.png','scalene', 20,12,'cm2');
triangle[6] = new Triangle('img/tri_q_area_6.png','scalene', 200,130,'cm2');
triangle[7] = new Triangle('img/tri_q_area_7.png','scalene', 20,16,'m2');
triangle[8] = new Triangle('img/tri_q_area_8.png','scalene', 28,16,'km2');
triangle[9] = new Triangle('img/tri_q_area_9.png','scalene', 54,38,'yards2');
// now you only need the index to address a specific element:
var correctIndex = 4;
var correctAnswer = triangle[correctIndex];
// visualize it in the console
console.log(correctAnswer);

Adding Multiple Arguments to a Custom Function in Google Sheets

I've searched high and wide for an answer but can't seem to find it. I am trying to alter my custom function that looks up sitemap URL's and the date they were updated to accept a range of inputs.
Here is the current function that works:
function sitemap(sitemapUrl, namespace) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9");
var urls = root.getChildren('url', sitemapNameSpace);
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array;
}
I've tried using Google's example below but doesn't seem to work however I incorporate it into my function. Any ideas?
function DOUBLE(input) {
if (input.map) { // Test whether input is an array.
return input.map(DOUBLE); // Recurse over array if so.
} else {
return input * 2;
}
}
Edit: This is how I tried to use Google's example for my function:
function sitemaps(sitemapUrl) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc',sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod',sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps);
} else {
return array
}
You are no using the same format as the Google example. As of right now you are checking if the input is an array after actually retrieving the data.
But you using fetch with an array as input could trigger an Error and the function may no get to the point where it checks if the sitemapUrl can be used with map.
Also take into account that map will call the function in every single element of the array and return an array with a result for each of element. So in your case B3:B6 would call the function for the value at B3, B4, B5 and B6 and return an array of length 4 with the result. For your case in which you want a single list you need to flattern the array afterwards
I would change your function to be like this:
function sitemaps(sitemapUrl) {
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps).flat();
} else {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array
}
}
Although what you are doing is fine take into account that it also exists a way to retrieve all the request at the same time (
UrlFetchApp.fetch()) but for this specific case you would need to flatten a reshape the input array.

2D dynamic javascript matrix for coordinates object

I'm trying to make a 2D matrix dynamic system which identifies whether there is an "object" at X,Y coordinates (true), or not (false).
Simplified example code:
var coords = [[]]; // Matrix is over 10,000 x 10,000
var objectX = 76;
var objectY = 54;
coords[objectX][objectY] = true;
//Check to see if there is an object # coordinates
if(coords[100][65] == false || coords[100][65] === undefined)
{
//There is no object # 100 x 65
}
else
{
//Object detected # 100 x 65
}
But it seems I can't do it this way, since I think I have to start from [0][0], [0][1], [0][2], ... , ect; or something..
Also, matrix is too large to define via putting it in a loop. I can't have it loading for hours.
I won't mind keeping an array segment 'undefined', as I treat it as false in my code.
How can I accomplish this?
You need to make sure the first dimension array exists before you address the second dimension:
if (coords[objectX] === undefined) coords[objectX] = [];
coords[objectX][objectY] = true;
If upfront you know you actually need an element for each X,Y position (which will consume more memory), then initialise the matrix first with a loop:
for (var objectX=0; objectX <= maxX; objectX++) {
coords[objectX] = [];
for (var objectY=0; objectY <= maxY; objectY++) {
coords[objectX][objectY] = false;
}
}
Depending on your needs, you might get better memory usage and performance if you would use a different structure:
var coords = [];
coords[objectX * (maxX + 1) + objectY] = true;
Or if you do not know the range of X nor Y:
coords = {}; // object whose properties will be X,Y strings:
coords[objectX + ',' + objectY] = true;

Identify each drawn path in Fabric JS

I am trying to identify each drawing.
For example after I draw an object I want that object to have the propriety name = "My Drawing 1" so I can easily refer to it later by
canvas.getObjectByName('My Drawing Name');
getObjectByName function
fabric.Canvas.prototype.getItemByName = function(name) {
var object = null, objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i].name && objects[i].name === name) {
object = objects[i];
break;
}
}
return object;
};
The code that activates drawing function:
canvas.isDrawingMode = true;
var img = $("#patternImg")[0];
var texturePatternBrush = new fabric.PatternBrush(canvas);
texturePatternBrush.source = img;
texturePatternBrush.selectable = true;
texturePatternBrush.name = img.getAttribute("data-name");
canvas.freeDrawingBrush = texturePatternBrush;
canvas.freeDrawingBrush.width = img.getAttribute("data-height");
Using the code above I can check if can verify if a drawing exists by:
fabric.Canvas.prototype.getDrawByName = function(name) {
var object = null, objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i].canvas.freeDrawingBrush.name && objects[i].canvas.freeDrawingBrush.name === name) {
object = objects[i];
break;
}
}
return object;
};
The problem is each time I am drawing an object that has another name canvas.FreeDrawingBrush.name will be the same for each object on my canvas.
I am not so familiar with FabricJS and I don't even know if what I am trying to do is even possible. At least I made myself clear what I am trying to do ?
When accessing the canvas property of a fabric.Object, you are simply getting a reference to the fabric.Canvas element that this object is rendered on. The freeDrawingBrush property of this fabric.Canvas element will always be the current free drawing brush.
Instead, I would set this name property onto each individual object, when it is done drawing. (This could be when you exit from drawing mode, or perhaps right before you switch to a different brush) using fabric.Object.set method.
Doing it this way, you aren't relying on an object's canvas property in any way.

Can't convert Javascript array to object properly

I have been struggling with this problem the entire day. I feel like it's super solvable, and I'm not sure where I'm going wrong. Each time I go to post this, I feel like I come up with a different solution that doesn't end up working.
I'm looking to do the following:
var someObj = {};
// #param key - string - in the form of "foo-bar-baz" or "foo-bar".
// (i won't know the number of segments ahead of time.)
// #param value - string - standard, don't need to act on it
function buildObject( key, value ) {
var allKeys = key.split("-");
// do stuff here to build someObj
}
Essentially the key will always take the format of key-key-key where i want to build someObj[key1][key2][key3] = value.
This JSFiddle contains a longer example with a sample layout of the data structure I want to walk away with.
Thanks so much for any help you can give.
var someObj = {};
// #param key - string - in the form of "foo-bar-baz" or "foo-bar".
// (i won't know the number of segments ahead of time.)
// #param value - string - standard, don't need to act on it
function buildObject( key, value ) {
var allKeys = key.split("-");
var container, i, n;
for (container = someObj, i = 0, n = allKeys.length; i < n - 1; ++i) {
var keyPart = allKeys[i];
container = Object.hasOwnProperty.call(container, keyPart)
? container[keyPart] : (container[keyPart] = {});
}
container[allKeys[n - 1]] = value;
}
I came up with http://jsfiddle.net/XAn4p/ before i saw Mike's answer. I'm posting just for another way.
var newObj = function ()
{};
newObj.prototype =
{
addToObject: function (keys, value)
{
var keySplit = keys.split("-",2);
if (keySplit.length > 1)
{
if(this[keySplit[0]] == null)
{
this[keySplit[0]] = new newObj();
}
var newKeys = keys.substr(keySplit[0].length +1);
this[keySplit[0]].addToObject(newKeys, value);
}
else
{
this[keySplit[0]] = value
}
}
};
var obj = new newObj();

Categories