Skip to content

Instantly share code, notes, and snippets.

@leefsmp
Created August 14, 2015 14:47
Show Gist options
  • Save leefsmp/a56413d9cc49575d3454 to your computer and use it in GitHub Desktop.
Save leefsmp/a56413d9cc49575d3454 to your computer and use it in GitHub Desktop.
Transform Tool Viewer Extension
///////////////////////////////////////////////////////////////////
// Transform Tool viewer extension
// by Philippe Leefsma, August 2015
//
///////////////////////////////////////////////////////////////////
AutodeskNamespace("Autodesk.ADN.Viewing.Extension");
Autodesk.ADN.Viewing.Extension.TransformTool = function (viewer, options) {
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
function TransformTool() {
var _hitPoint = null;
var _isDragging = false;
var _transformMesh = null;
var _modifiedFragIdMap = {};
var _selectedFragProxyMap = {};
var _transformControlTx = null;
///////////////////////////////////////////////////////////////////////////
// Creates a dummy mesh to attach control to
//
///////////////////////////////////////////////////////////////////////////
function createTransformMesh() {
var material = new THREE.MeshPhongMaterial(
{ color: 0xff0000 });
viewer.impl.matman().addMaterial(
guid(),
material,
true);
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.0001, 5),
material);
sphere.position.set(0, 0, 0);
return sphere;
}
///////////////////////////////////////////////////////////////////////////
// on translation change
//
///////////////////////////////////////////////////////////////////////////
function onTxChange() {
for(var fragId in _selectedFragProxyMap) {
var fragProxy = _selectedFragProxyMap[fragId];
var position = new THREE.Vector3(
_transformMesh.position.x - fragProxy.offset.x,
_transformMesh.position.y - fragProxy.offset.y,
_transformMesh.position.z - fragProxy.offset.z);
fragProxy.position = position;
fragProxy.updateAnimTransform();
}
viewer.impl.sceneUpdated(true);
}
///////////////////////////////////////////////////////////////////////////
// on camera changed
//
///////////////////////////////////////////////////////////////////////////
function onCameraChanged() {
_transformControlTx.update();
}
///////////////////////////////////////////////////////////////////////////
// item selected callback
//
///////////////////////////////////////////////////////////////////////////
function onItemSelected(event) {
_selectedFragProxyMap = {};
//component unselected
if(!event.fragIdsArray.length) {
_hitPoint = null;
_transformControlTx.visible = false;
_transformControlTx.removeEventListener(
'change', onTxChange);
viewer.removeEventListener(
Autodesk.Viewing.CAMERA_CHANGE_EVENT,
onCameraChanged);
return;
}
if(_hitPoint) {
_transformControlTx.visible = true;
_transformControlTx.setPosition(_hitPoint);
_transformControlTx.addEventListener(
'change', onTxChange);
viewer.addEventListener(
Autodesk.Viewing.CAMERA_CHANGE_EVENT,
onCameraChanged);
event.fragIdsArray.forEach(function (fragId) {
var fragProxy = viewer.impl.getFragmentProxy(
viewer.model,
fragId);
fragProxy.getAnimTransform();
var offset = {
x: _hitPoint.x - fragProxy.position.x,
y: _hitPoint.y - fragProxy.position.y,
z: _hitPoint.z - fragProxy.position.z
};
fragProxy.offset = offset;
_selectedFragProxyMap[fragId] = fragProxy;
_modifiedFragIdMap[fragId] = {};
});
_hitPoint = null;
}
else {
_transformControlTx.visible = false;
}
}
///////////////////////////////////////////////////////////////////////////
// normalize screen coordinates
//
///////////////////////////////////////////////////////////////////////////
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;
}
///////////////////////////////////////////////////////////////////////////
// get 3d hit point on mesh
//
///////////////////////////////////////////////////////////////////////////
function getHitPoint(event) {
var screenPoint = {
x: event.clientX,
y: event.clientY
};
var n = normalize(screenPoint);
var hitPoint = viewer.utilities.getHitPoint(n.x, n.y);
return hitPoint;
}
///////////////////////////////////////////////////////////////////////////
// returns all transformed meshes
//
///////////////////////////////////////////////////////////////////////////
this.getTransformMap = function() {
var transformMap = {};
for(var fragId in _modifiedFragIdMap){
var fragProxy = viewer.impl.getFragmentProxy(
viewer.model,
fragId);
fragProxy.getAnimTransform();
transformMap[fragId] = {
position: fragProxy.position
};
fragProxy = null;
}
return transformMap;
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.getNames = function() {
return ['Dotty.Viewing.Tool.TransformTool'];
};
this.getName = function() {
return 'Dotty.Viewing.Tool.TransformTool';
};
///////////////////////////////////////////////////////////////////////////
// activates tool
//
///////////////////////////////////////////////////////////////////////////
this.activate = function() {
viewer.select([]);
var bbox = viewer.model.getBoundingBox();
viewer.impl.createOverlayScene(
'Dotty.Viewing.Tool.TransformTool');
_transformControlTx = new THREE.TransformControls(
viewer.impl.camera,
viewer.impl.canvas,
"translate");
_transformControlTx.setSize(
bbox.getBoundingSphere().radius * 5);
_transformControlTx.visible = false;
viewer.impl.addOverlay(
'Dotty.Viewing.Tool.TransformTool',
_transformControlTx);
_transformMesh = createTransformMesh();
_transformControlTx.attach(_transformMesh);
viewer.addEventListener(
Autodesk.Viewing.SELECTION_CHANGED_EVENT,
onItemSelected);
};
///////////////////////////////////////////////////////////////////////////
// deactivate tool
//
///////////////////////////////////////////////////////////////////////////
this.deactivate = function() {
viewer.impl.removeOverlay(
'Dotty.Viewing.Tool.TransformTool',
_transformControlTx);
_transformControlTx.removeEventListener(
'change',
onTxChange);
_transformControlTx = null;
viewer.impl.removeOverlayScene(
'Dotty.Viewing.Tool.TransformTool');
viewer.removeEventListener(
Autodesk.Viewing.CAMERA_CHANGE_EVENT,
onCameraChanged);
viewer.removeEventListener(
Autodesk.Viewing.SELECTION_CHANGED_EVENT,
onItemSelected);
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.update = function(t) {
return false;
};
this.handleSingleClick = function(event, button) {
return false;
};
this.handleDoubleClick = function(event, button) {
return false;
};
this.handleSingleTap = function(event) {
return false;
};
this.handleDoubleTap = function(event) {
return false;
};
this.handleKeyDown = function(event, keyCode) {
return false;
};
this.handleKeyUp = function(event, keyCode) {
return false;
};
this.handleWheelInput = function(delta) {
return false;
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.handleButtonDown = function(event, button) {
_hitPoint = getHitPoint(event);
_isDragging = true;
if (_transformControlTx.onPointerDown(event))
return true;
//return _transRotControl.onPointerDown(event);
return false;
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.handleButtonUp = function(event, button) {
_isDragging = false;
if (_transformControlTx.onPointerUp(event))
return true;
//return _transRotControl.onPointerUp(event);
return false;
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.handleMouseMove = function(event) {
if (_isDragging) {
if (_transformControlTx.onPointerMove(event) ) {
return true;
}
return false;
}
if (_transformControlTx.onPointerHover(event))
return true;
//return _transRotControl.onPointerHover(event);
return false;
};
///////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////
this.handleGesture = function(event) {
return false;
};
this.handleBlur = function(event) {
return false;
};
this.handleResize = function() {
};
}
Autodesk.Viewing.Extension.call(this, viewer, options);
var _self = this;
_self.tool = null;
///////////////////////////////////////////////////////
// extension load callback
//
///////////////////////////////////////////////////////
_self.load = function () {
_self.tool = new TransformTool();
viewer.toolController.registerTool(_self.tool);
viewer.toolController.activateTool(_self.tool.getName());
console.log('Autodesk.ADN.Viewing.Extension.TransformTool loaded');
return true;
};
///////////////////////////////////////////////////////
// extension unload callback
//
///////////////////////////////////////////////////////
_self.unload = function () {
viewer.toolController.deactivateTool(_self.tool.getName());
console.log('Autodesk.ADN.Viewing.Extension.TransformTool unloaded');
return true;
};
///////////////////////////////////////////////////////
// new random guid
//
///////////////////////////////////////////////////////
function guid() {
var d = new Date().getTime();
var guid = 'xxxx-xxxx-xxxx-xxxx-xxxx'.replace(
/[xy]/g,
function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return guid;
};
};
Autodesk.ADN.Viewing.Extension.TransformTool.prototype =
Object.create(Autodesk.Viewing.Extension.prototype);
Autodesk.ADN.Viewing.Extension.TransformTool.prototype.constructor =
Autodesk.ADN.Viewing.Extension.TransformTool;
Autodesk.Viewing.theExtensionManager.registerExtension(
'Autodesk.ADN.Viewing.Extension.TransformTool',
Autodesk.ADN.Viewing.Extension.TransformTool);
@pangzx1
Copy link

pangzx1 commented Sep 22, 2020

Hello, I am trying to use this extension you wrote, but I have encountered some problems. (The version of Forge Viewer I use is 6.*) My questions are as follows:
error code:
Uncaught TypeError: Cannot read property 'getBoundingBox' of null

Uncaught TypeError: Cannot read property 'onPointerHover' of null
As shown in the picture below
Snipaste_2020-09-22_14-31-21

My code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Custom Geometry</title>
    <link
      rel="stylesheet"
      href="https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/style.min.css"
      type="text/css"
    />
    <script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/viewer3D.min.js"></script>
    <link rel="stylesheet" href="./css/stylesheet.css" type="text/css" />

	<script src="js/TransformTool.js"></script>
  </head>
  <body>
    
    <div id="ForgeViewerDiv"></div>
  </body>
  <script>
    const divID = "ForgeViewerDiv";
    const documentId =
      "urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6YWRhbGJpc3MtZm9yZ2UtYXUtMjAxOS9Nb2RlbC1Sb29tLnJ2dA";
    const tokenFetchingUrl =
      "https://k9mfy8k8a0.execute-api.us-east-2.amazonaws.com/prod";

    let extensionArray = ["Autodesk.ADN.Viewing.Extension.TransformTool"];

    let viewerApp = new Autodesk.Viewing.ViewingApplication(divID);
    let viewer = null;

    let options = {
      env: "AutodeskProduction",
      useConsolidation: false,
      getAccessToken: onGetAccessToken => {
        fetch(tokenFetchingUrl)
          .then(response => response.json())
          .then(data => {
            let accessToken = data["access_token"];
            let expireTimeSeconds = data["expires_in"];
            onGetAccessToken(accessToken, expireTimeSeconds);
          });
      }
    };

    let config3d = {
      extensions: extensionArray
    };

    Autodesk.Viewing.Initializer(options, function onInitialized() {
      viewerApp.registerViewer(
        viewerApp.k3D,
        Autodesk.Viewing.Private.GuiViewer3D,
        config3d
      );
      viewerApp.loadDocument(
        documentId,
        onDocumentLoadSuccess,
        onDocumentLoadFailure
      );
    });

    // Init after the viewer is ready
    function onDocumentLoadSuccess() {
      let viewables = viewerApp.bubble.search({
        type: "geometry"
      });
      if (viewables.length === 0) {
        console.error("Document contains no viewables.");
        return;
      }
      // Choose any of the available viewables
      viewerApp.selectItem(
        viewables[0].data,
        onItemLoadSuccess,
        onItemLoadFail
      );
    }

    function onDocumentLoadFailure(viewerErrorCode) {
      console.error("onDocumentLoadFailure() - errorCode:" + viewerErrorCode);
    }

    function onItemLoadSuccess(active_viewer, item) {
      console.log("Document loaded successfully");
      viewer = active_viewer;
    }
    function onItemLoadFail(errorCode) {
      console.error("onItemLoadFail() - errorCode:" + errorCode);
    }
  </script>
</html>

Do you know the reason?
I look forward to receiving your reply, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment