By Philippe Leefsma (@F3lipek)
Here is a reworked version of one of my early sample for View & Data API: This illustrates how you can create markups pointing to a specific point on your model. The main feature is that the markups display a 2D symbol on the canvas and is hooked up to camera events, so when rotating the model, the 3D world point changes position and so does the 2D symbol, giving the impression that the markup sticks to that point.
There are few remarks about the implementation that I wanted to highlight in this post:
I extensively used SVG to handle any 2D symbol on top of the WebGL canvas: this is clearly what gives the most flexibility and nicest visuals as far as 2D graphics are concerned. To manipulate SVG, I find Snap.svg pretty handy, a thin wrapper I already blogged about previously.
I am splitting the source into several files as it makes sense and also facilitate code reuse, then I rely on a build step using webpack to bundle all those files, including also the .css, into a single .js that can easily be loaded by my main application. Also webpack makes it very easy to use Babel to transpile code to enjoy all the benefits of ES2015 modern syntax.
Here are how the extension files look like:
The entry point is Viewing.Extension.Markup3D.js but most the logic is implemented in the tool and LeaderNote classes. One thing that is interesting to point out is that the markups can be stored as part of the state. This isn't a documented feature, but if you look into the source code for viewer.getState and viewer.restoreState you will see that those methods are iterating through the loaded extensions in order to let them inject or restore custom state data.
In order to use that feature, all you need to do is implementing those same methods in your extension, which looks has below in my sample:
1 ///////////////////////////////////////////////////////////////// 2 // 3 // From viewer.getState: 4 // Allow extensions to inject their state data 5 // 6 // for (var extensionName in viewer.loadedExtensions) { 7 // viewer.loadedExtensions[extensionName].getState( 8 // viewerState); 9 // } 10 ///////////////////////////////////////////////////////////////// 11 getState(viewerState) { 12 13 this.markup3DTool.getState( 14 viewerState); 15 } 16 17 ///////////////////////////////////////////////////////////////// 18 // 19 // From viewer.restoreState: 20 // Allow extensions to restore their data 21 // 22 // for (var extensionName in viewer.loadedExtensions) { 23 // viewer.loadedExtensions[extensionName].restoreState( 24 // viewerState, immediate); 25 // } 26 ///////////////////////////////////////////////////////////////// 27 restoreState(viewerState, immediate) { 28 29 this.markup3DTool.restoreState( 30 viewerState, immediate); 31 }
The complete implementation is available from there: Viewing.Extension.Markup3D
And here is a sample you can play with. It also includes the StateManager so you can save and restore markups. Click any point on the model to create new markups, double click a label to edit the markup properties, drag an existing label to modify its position.
Hi,
I've got the whole library and tried webpack.
at first I received a 'typescript' related error, after excluding typescript from the config file webpack ran successfully and generated .min files.
I copied the "Viewing.Extension.Markup3D.min.js" to my extension folders and tried loading it but I receive this error:
"Viewing.Extension.Markup3D.min.js:4 Uncaught ReferenceError: regeneratorRuntime is not defined"
Can you please help where to start to fix the issue?
regards,
Posted by: Afshin Jafari | 06/08/2016 at 08:52 PM
No worries, you need to include babel-polyfill script or package (depending if you are using webpack in your app) before any babel transpiled code:
https://babeljs.io/docs/usage/polyfill/
Regards,
Philippe.
Posted by: Philippe Leefsma | 06/09/2016 at 01:06 AM
Thanks Philippe,
Yes I thought that would be the case and tried (sorry I had to mention in my comment). so I installed polyfill (npm install --save-dev babel-polyfill) and added "import "babel-polyfill";" on top of the "Viewing.Extension.Markup3D.js" then ran the webpack. although it made the generated min file bigger but still I receive the same error.
Regards
Afshin
Posted by: Afshin Jafari | 06/09/2016 at 02:32 PM
You have to add the polyfill on top of your app, not on top of Viewing.Extension.Markup3D.js or any other files from my library.
This error is definitely caused by an improper loading of the polyfill. It has to be loaded before any other script with has been transpiled.
Posted by: Philippe Leefsma | 06/09/2016 at 04:12 PM
ah.. thanks, so moved it to the top and that error disappeared. now I see this:
Uncaught TypeError: Cannot read property 'registerTool' of undefined
any suggestions?
Regards,
Afshin
Posted by: Afshin Jafari | 06/09/2016 at 04:18 PM
That's thrown by the line in Markup3DExtension.contructor:
this._viewer.toolController.registerTool(
this.markup3DTool)
Try loading the extension in GEOMETRY_LOADED_EVENT ...
Posted by: Philippe Leefsma | 06/10/2016 at 01:03 AM
Hi, Philippe Leefsma,
I don't know where to directly contact you. I read your content in https://stackoverflow.com/questions/46190805/autodesk-forge-markup-extension-error-create-switch-button-error#new-answer. I Would like to see your Markup UI sample but the provided link is dead. Could you please provide the active link. Your help will be highly appreciated. Thank you.
Posted by: Bon_juillet | 04/12/2020 at 06:23 AM