在 Autodesk Forge 查看器中的元素下拾取
Posted
技术标签:
【中文标题】在 Autodesk Forge 查看器中的元素下拾取【英文标题】:Picking under elements of an element in Autodesk Forge viewer 【发布时间】:2017-05-07 01:14:21 【问题描述】:我想完成一项可以在 Three.js 中完成但在 Autodesk Forge 查看器中无法完成的功能。以下是测试链接:http://app.netonapp.com/javascript/Three.js/select_inner_objects.html
要求是选择对象内的对象。这项工作可以在上面的演示中使用 THREE.Raycaster 来完成,使用 raycaster 来检测光线通过线上的所有元素。然后我可以在另一个对象的后面或内部获取对象。
我在 Autodesk Forge 查看器中尝试了这个概念,但没有成功。代码如下:
// Change this to:
// true to use original Three.js
// false to use Autodesk Forge Viewer API
var useThreeJS = true;
var container = $('div.canvas-wrap')[0];
container.addEventListener('mousedown', function (event)
if (useThreeJS)
var canvas = _viewer.impl.canvas;
var containerWidth = canvas.clientWidth;
var containerHeight = canvas.clientHeight;
var camera = _viewer.getCamera();
var mouse = mouse || new THREE.Vector3();
var raycaster = raycaster || new THREE.Raycaster();
mouse.x = 2 * (event.clientX / containerWidth) - 1;
mouse.y = 1 - 2 * (event.clientY / containerHeight);
mouse.unproject(camera);
raycaster.set(camera.position, mouse.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(objects);
if (intersects.length == 1)
var obj = intersects[0].object;
obj.material.color.setRGB(1.0 - i / intersects.length, 0, 0);
else if (intersects.length > 1)
// Exclude the first which is the outer object (i == 0)
for (var i = 1; i < intersects.length; i++)
var obj = intersects[i].object;
obj.material.color.setRGB(1.0 - i / intersects.length, 0, 0);
else
var vp = _viewer.impl.clientToViewport(event.canvasX, event.canvasY);
var renderer = _viewer.impl.renderer();
var dbId = renderer.idAtPixel(vp.x, vp.y);
if (dbId)
console.debug("Selected Id: " + dbId);
_viewer.select(dbId);
_viewer.impl.invalidate(true);
, false);
我发现 Forge 查看器有 viewer.impl.renderer().idAtPixel
方法,该方法非常适合在拾取像素处获取元素。但是,我希望它做更多的事情,在拾取像素处选择所有元素(在下面或嵌套)。如何使用 Forge Viewer API 做到这一点?
【问题讨论】:
【参考方案1】:根据另一个post中吴钟的建议,这里是选择另一个元素下或内部的元素的最终解决方案。我创建了一个 Autodesk Forge 查看器扩展来轻松使用它。
///////////////////////////////////////////////////////////////////////////////
// InnerSelection viewer extension
// by Khoa Ho, December 2016
//
///////////////////////////////////////////////////////////////////////////////
AutodeskNamespace("Autodesk.ADN.Viewing.Extension");
Autodesk.ADN.Viewing.Extension.InnerSelection = function (viewer, options)
Autodesk.Viewing.Extension.call(this, viewer, options);
var _self = this;
var _container = viewer.canvas.parentElement;
var _renderer = viewer.impl.renderer();
var _instanceTree = viewer.model.getData().instanceTree;
var _fragmentList = viewer.model.getFragmentList();
var _eventSelectionChanged = false;
var _viewport;
var _outerDbId;
_self.load = function ()
_container.addEventListener('mousedown',
onMouseDown);
viewer.addEventListener(
Autodesk.Viewing.SELECTION_CHANGED_EVENT,
onItemSelected);
console.log('Autodesk.ADN.Viewing.Extension.InnerSelection loaded');
return true;
;
_self.unload = function ()
_container.removeEventListener('mousedown',
onMouseDown);
viewer.removeEventListener(
Autodesk.Viewing.SELECTION_CHANGED_EVENT,
onItemSelected);
console.log('Autodesk.ADN.Viewing.Extension.InnerSelection unloaded');
return true;
;
function onMouseDown(e)
var viewport = viewer.impl.clientToViewport(e.canvasX, e.canvasY);
_viewport = viewport; // Keep this viewport to use in onItemSelected()
var dbId = _renderer.idAtPixel(viewport.x, viewport.y);
if (_outerDbId == dbId)
_outerDbId = -1;
// Deselect everything
viewer.select();
else
_outerDbId = dbId;
// Hide outer element temporarily to allow picking its behind element
viewer.hideById(dbId);
_eventSelectionChanged = true;
viewer.impl.sceneUpdated(true);
function onItemSelected(e)
if (_eventSelectionChanged)
// Prevent self looping on selection
_eventSelectionChanged = false;
// Show outer element back
viewer.show(_outerDbId);
// Get inner element Id after the outer element
// was just hidden on mouse down event
var innerDbId = _renderer.idAtPixel(_viewport.x, _viewport.y);
if (innerDbId > -1)
// Select the inner element when it is found
viewer.select(innerDbId);
console.debug("Selected inner Id: " + innerDbId);
else if (_outerDbId > -1)
// Select the outer element if the inner element is not found
viewer.select(_outerDbId);
console.debug("Selected outer Id: " + _outerDbId);
;
Autodesk.ADN.Viewing.Extension.InnerSelection.prototype =
Object.create(Autodesk.Viewing.Extension.prototype);
Autodesk.ADN.Viewing.Extension.InnerSelection.prototype.constructor =
Autodesk.ADN.Viewing.Extension.InnerSelection;
Autodesk.Viewing.theExtensionManager.registerExtension(
'Autodesk.ADN.Viewing.Extension.InnerSelection',
Autodesk.ADN.Viewing.Extension.InnerSelection);
【讨论】:
【参考方案2】:我看到方法viewer.impl.renderer().idAtPixel
比viewer.impl.hitTestViewport
更好地选择鼠标选择元素。第一个可以通过hidden/ghost元素点击获取后面元素的objectId。而第二个不能。这是要测试的代码:
var container = $('div.canvas-wrap')[0];
container.addEventListener('mousedown', function (event)
var clickThroughHiddenElement = true;
if (clickThroughHiddenElement)
var vp = _viewer.impl.clientToViewport(event.canvasX, event.canvasY);
var renderer = _viewer.impl.renderer();
var dbId = renderer.idAtPixel(vp.x, vp.y);
if (!!dbId)
_viewer.select(dbId);
console.debug("Selected Id: " + dbId);
else
var screenPoint =
x: event.clientX,
y: event.clientY
;
var viewport = _viewer.navigation.getScreenViewport();
var x = (screenPoint.x - viewport.left) / viewport.width;
var y = (screenPoint.y - viewport.top) / viewport.height;
// Normalize point
x = x * 2.0 - 1.0;
y = (1.0 - y) * 2.0 - 1.0;
var vpVec = new THREE.Vector3(x, y, 1);
var result = _viewer.impl.hitTestViewport(vpVec, false);
if (!!result)
var dbId = result.dbId;
_viewer.select(dbId);
console.debug("Selected Id: " + dbId);
但是,它们不是我想要的,单击透明元素以获取后面的元素。如果用户选择透明元素,它将被选中。如果用户选择内部元素,它将忽略外部透明元素来选择内部元素。
我检查了 Forge 查看器使用带有元素边界框的 THREE.Raycaster 来检测鼠标单击时的交叉点。看来我的问题在 Forge 查看器中是可行的,就像在我的 Three.js demo 中一样。
【讨论】:
【参考方案3】:截至目前(12 月 16 日),当您使用鼠标单击选择时,查看器将不会忽略透明元素,因此即使它是透明的,它也会选择一个元素。下面是我用来跟踪光标下的内容的代码,也许会有用。
// use jQuery to bind a mouve move event
$(_viewer.container).bind("mousemove", onMouseMove);
function onMouseMove(e)
var screenPoint =
x: event.clientX,
y: event.clientY
;
var n = normalize(screenPoint);
var dbId = /*_viewer.utilities.getHitPoint*/ getHitDbId(n.x, n.y);
//
// use the dbId somehow...
//
// This is a built-in method getHitPoint, but the original returns
// the hit point, so this modified version returns the dbId
function getHitDbId()
y = 1.0 - y;
x = x * 2.0 - 1.0;
y = y * 2.0 - 1.0;
var vpVec = new THREE.Vector3(x, y, 1);
var result = _viewer.impl.hitTestViewport(vpVec, false);
//return result ? result.intersectPoint : null; // original implementation
return result ? result.dbId : null;
function normalize(screenPoint)
var viewport = _viewer.navigation.getScreenViewport();
var n =
x: (screenPoint.x - viewport.left) / viewport.width,
y: (screenPoint.y - viewport.top) / viewport.height
;
return n;
【讨论】:
viewer.impl.hitTestViewport
无法选择隐藏/幽灵元素后面的元素。 viewer.impl.renderer().idAtPixel
效果更好。以上是关于在 Autodesk Forge 查看器中的元素下拾取的主要内容,如果未能解决你的问题,请参考以下文章
在 Autodesk Forge Viewer 中对齐坐标系
如何在 nuxt js Autodesk forge 查看器中使用 IconMarkupExtension?
如何在 Autodesk Forge 查看器中更改选择颜色?