cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Your Friends List is a way to easily have access to the community members that you interact with the most! X

Vuforia Studio and Chalk Tech Tips

Sort by:
Vuforia Studio The source URL of 3D Audio and 3D Video widgets can now be set dynamically Bug fixes and minor improvements Vuforia View Android Versions prior to 8.0 are no longer supported RealWear Beginning in August 2021, only RealWear 12.1 will be supported HoloLens Improvements to stability of Model Tracking Improved display of models defined at large scales Bug fixes and minor improvements Experience Service Bug fixes and security improvements
View full tip
Vuforia Studio Support for sequences created with Creo Illustrate 8.0 Support for occlusion on Area Targets Improvements to 3D Panel widget: Ability to configure offset and snap distance Bug fixes and minor improvements Vuforia View Support for Windows Surface Go 2 Android: Support for Android 6 and 7 will be discontinued in July 2021 RealWear: Support for RealWear 11 will be discontinued in July 2021 Bug fixes and minor improvements Experience Service Support for ThingWorx 9.2 Support for RHEL 8.4 Support for Ubuntu 20.04 Support for PostgreSQL 13.3 Bug fixes and minor improvements
View full tip
With release 9.1.0, Vuforia Studio now supports Area Targets. Area Targets can be used when creating spatial instructions using the surroundings as interactive elements to be explored. For example, you can use an Area Target when you want to deliver augmentations to stationary objects in a scanned environment. You'll need to create an Area Target before you can import it into Vuforia Studio to leverage the Area Target widget. Below is an overview of the required steps to create and upload Area Targets for Vuforia Studio.   Step 1: Create a Vuforia Engine Developer Account   This account is separate from your regular PTC support account and is required to generate Area Targets.   Step 2: Download a Target Generator Once you've created a Developer account, you'll need to download a target generator. The target generator you download will depend on the device used to scan your environment.   Vuforia Studio currently supports scans created with: Matterport Pro2 Camera Requires the Area Target Generator desktop application. iOS devices with LiDAR sensors Requires the iOS app Vuforia Area Target Creator to scan and create Area Targets Step 3: Create an Area Target   Creating an Area Target using the Matterport Pro2 Camera:   Log into the Vuforia Area Target Generator with your Vuforia Developer Credentials Select New Area Target from Matterport Scan Enter your Matterport token information See Retrieving Models with the Matterport™ API Token for instructions on obtaining your token ID Enter the Scanned Space ID and Area Target Name The Scanned Space field is the Matterport™ SpaceID and a unique 11-digit ID that is associated with your individual scans. Find the unique ID of your scan by opening the scanned space from your Matterport™ account. Copy the entire URL, or just the SpaceID portion as illustrated and paste it into the Scanned Space  field. Area Target name must not contain any spaces Select Create Area Target to generate an Area Target of your scan. Full instructions for creating a scan and area target using the Matterport Pro2 Camera can be found here.   Creating an Area Target using the Vuforia Area Target Creator (requires internet connection): Login with your Vuforia Developer Credentials from step 1. Select the Plus Button to create a new scan and enter a scan name Start the scan Press the Stop Button to finish the scan Select the Legacy Dataset option, then select Generate to generate an Area Target database     Step 4: Prepare your files for Studio   NOTE: All filenames are case sensitive   Area Targets created with the Vuforia Area Target Generator: Navigate to the location you entered when creating your Area Target Location defaults to C:/Users/<username>/Documents/Vuforia Areas Create a zip file titled <target_name>.zip that contains ONLY: <target_name>.dat <target_name>.xml <target_name>_authoring.glb Area Targets created with the Vuforia Area Target Creator: Share your files to your Studio authoring machine Press the Container button and swipe one of the Area Target datasets listed to the left. A share icon will appear. Press the Share icon next to a listed dataset Select your transfer method It is possible to manually pull the files directly from your device. Using iTunes or a similar file explorer, navigate to the app's AreaTargetData folder and copy the desired Area Target directory.  Ensure that your <target_name>.zip file contains ONLY: <target_name>.dat <target_name>.xml <target_name>_authoring.glb Step 5: Upload your Area Target file to Vuforia Studio Add an Area Target widget your canvas Select the green plus button next to Data set  Browse to the location of your <target_name>.zip file and select open   See our Help Center for more information on Area Targets in Vuforia Studio.
View full tip
Vuforia Studio Area Targets are now supported! Area Targets bring Vuforia-powered environment tracking into your Experiences enabling you to track and augment areas and spaces. Area Targets allow users to easily navigate to the appropriate content using spatial clues, ultimately improving operational efficiency and work safety. Bug fixes and minor improvements Vuforia View Italian and Spanish languages now supported for HoloLens 2 voice commands RealWear: Fixes to provide better support and performance for RealWear OS 12; in order to leverage these enhancements, projects will need to be republished. There are a few known issues that have been reported to RealWear. For more information, see Article - CS345904 Bug fixes and minor improvements Experience Service Bug fixes and minor improvements
View full tip
Vuforia Studio Bug fixes and minor improvements Vuforia View HoloLens: New swipe right action brings you to downloaded Experiences French language now supported for HoloLens 2 voice commands Bug fixes and minor improvements Experience Service New Experience Service Help Center that includes all information needed to install and deploy an Experience Service Support for Red Hat Enterprise Linux (RHEL) 8.2 Ability to retrieve information about published projects including: Project file size Publish date Username that published the project Bug fixes and minor improvements
View full tip
The new 3D-Guided Service Instructions example use case walks you through creating a Vuforia Studio Experience that will allow a frontline worker to:  Find the physical location of a broken part on an object using a digital model  Search for parts on a 3D model of the object  Find and order replacement parts from a vendor using a persistent shopping cart   The 3D-Guided Service Instructions use case will walk you through the following sections: 3D-Guided Service Instructions 101: Use Attributes in Creo Illustrate  3D-Guided Service Instructions 201: Use JavaScript to Highlight Parts and Create Ionic Popups  3D-Guided Service Instructions 202: Use JavaScript to Find Parts  3D-Guided Service Instructions 301: Add Pricing Data and a Shopping Cart to a Model  3D-Guided Service Instructions 302: Add a Simple ThingWorx Service to Vuforia Studio  3D-Guided Service Instructions 303: Create a Persistent Shopping Cart Using ThingWorx
View full tip
Vuforia Studio New 3D-Guided Service Instructions tutorial that provides access to project files and step-by-step instructions Bug fixes and minor improvements Vuforia View Support for Japanese voice commands on HoloLens 2 Bug fixes and minor improvements Experience Service Bug fixes and minor improvements
View full tip
Vuforia Studio New widgets for 3D Eyewear projects: 3D Press Button We recommend using a 3D Press Button in place of the 3D Button, as the 3D Button widget will be deprecated in a future release. A few things to keep in mind: In May, the 3D Button widget will no longer available for new projects. This will not immediately affect existing projects that contain 3D Button widgets, and you will still be able to edit and re-publish projects that contain them. In the future (TBD) all existing 3D Buttons will be transitioned to 3D Press Buttons. More information on this transition will be forthcoming. 3D Image Button 3D Toggle Button 3D Checkbox 3D Panel Ability to pin and unpin 3D Audio and 3D Video widgets in runtime German and Simplified Chinese now fully supported for HoloLens 2 voice commands If your HoloLens was set to English when you installed Vuforia View, you'll need to delete it and reinstall after you've changed your language setting to Simplified Chinese or German Bug fixes and minor improvements Vuforia View Bug fixes and minor improvements Experience Service Support for PostgreSQL 11.6 and 12.4 Bug fixes and minor improvements
View full tip
Vuforia Studio New Tagalong property available for 3D Video and 3D Audio widgets Improvements to multi-select for 3D widgets Ability to delete multiple widgets Ability to assign common widget properties a value Audio widget is now available in 2D Eyewear projects Enable Tracking Events property now available on 3D Container for 3D Eyewear projects Bug fixes and minor improvements Vuforia View HoloLens: 3D audio and video widgets will now stay in a user's line of view if the Tagalong property has been enabled in Vuforia Studio Bug fixes and minor improvements Experience Service Support for PingFederate 9.3.3 Patch 5 or later (SSO only) Bug fixes and minor improvements
View full tip
Vuforia Studio Support for Creo Illustrate 6.1 Decals Ability to select multiple 3D widgets on the canvas or in the project tree using the CTRL key Bug fixes and minor improvements Vuforia View Official support for Simplified Chinese and German languages on HoloLens 2 NOTE: Other languages are available, but are not officially supported yet.  See our Help Center for more information on available and supported languages. Support for Microsoft Surface Pro 7 Support for Trimble XR10 Support for RealWear Firmware version 11.2 Bug fixes and minor improvements Experience Service Support for ThingWorx 9.1 Bug fixes and minor improvements   In an effort to improve the 3D Eyewear authoring experience, we are developing a 3D Panel container widget. However, it was mistakenly included with the 9.0.0 release, and should not be used, as it is not functional. 
View full tip
Vuforia Studio New 3D Audio Widget Improvements to the 3D Video Widget for 3D Eyewear projects Ability to change the color of the buttons and panel Vuforia View iOS 12 is no longer supported Bug fixes and minor improvements   Experience Service Bug fixes and minor improvements 
View full tip
Vuforia Studio New 3D Video widget for 3D Eyewear projects Bug fixes and improvements Vuforia View Camera widget for mobile now supported on Windows devices iOS 12 will no longer be supported as of December Support for Vuzix M400 Bug fixes and minor improvements Experience Service Support for PostgreSQL 9.6 and 10.14 Bug fixes and minor improvements
View full tip
In Vuforia Studio version 8.5.13 a new metadata is intruduced. This will make the techniques described e.g. in the posts "How to extract the components with properties from a pvz file","Extracting the viewables and the seqnece steps information from a .pvz file for the usage in TWX" and "How to extract model data of 3d models in Vuforia Studio (without external Tools)?"  for the most cases not neccessarly any more. as In Vuforia Studio when a model is imported (add Resource) there is a new check button “Allow the Experience access to CAD metadata” :   The selection of this checkbox lead that when the model is loaded to the “app/resources/Uploaded” folder but also an a json object with the name <model name>.metadata.json Example when we load the model “Coffee Maker Model_High.pvz” then we have also a json file named “Coffee Maker Model_High.metadata.json”. This JSON file contains the metadata to the coffee maker model   The metadata json file contains some different sections for each component / For each component / CompPath Id  we have a sub object – comp obj {"/":{"":{"Adapter_name":"proepview",… "/11":{"":{"Feature_Id":"11","Source_file_name": … "/14":{"":{"Feature_Id":"14","Source_file_name": … Where “/” means the root asm , “/11” and “/14” are components paths The component object contains the following sections : general(empty string as key of the json obj) “”, "PROE Parameters" and "__PV_SystemProperties" (see the attached example “Coffee Maker Model_High.metadata.json”) The one possible approach could be , to use only this json object directly  but we can use also the PTC API which is provided starting with the Vuforia Studio 8.5.13 Metadata Access: Read the json object into memory (javascript):  In this case we can use the json object which was already created when the model data is omported in Studio.         var metaDataArray=[]; //////////////////////////////////////////////////////// //========================== When Model Loaded Event $rootScope.$on("modelLoaded", function() { if (arguments.length >0){ //================== if args >1 ====================== var modelWidgetId=arguments[1]; metaDataArray[modelWidgetId]={}; console.warn($scope.view.wdg); let wdg= $scope.view.wdg[modelWidgetId]; let mdlsrc=$scope.getWidgetProp(modelWidgetId,'src'); console.log( "mdlsrc="+mdlSrc); //==== extracts the model file name with extension let mdlNameExt= mdlSrc.replace(/^.*[\\\/]/, ''); console.log( "mdlNameExt="+mdlNameExt); //==== extracts the model file name without extension var mdlName=mdlNameExt.replace(/\.[^/.]+$/, ""); console.log( "mdlName="+mdlName); // ---adds the modelname to the array element for the widget metaDataArray[modelWidgetId]['mdlName']=mdlName; metaDataArray[modelWidgetId]['CompIdList']=[]; // $https call of the JSON file form the UPLOAD folder $http.get('app/resources/Uploaded/' + metaDataArray[modelWidgetId]['mdlName']+'.metadata.json') .success(function(data, status, headers, config) {//-------- success fnc metaDataArray[modelWidgetId]['data']=data; angular.forEach(data , function(value ,key){ //-------- ForEach json loop metaDataArray[modelWidgetId]['CompIdList'].push(key); });//--------end of ForEach json loop // print the data to the console console.log("metaDataArray[]"); console.warn(metaDataArray); })//-------- end success fnic .error(function(data, status, headers, config) {console.log("problem in the http will create a new ");}); //////////////////////////////////////////////////////// })          The code listed above will load the json file to the modelWidget/s (it will works also if we have many model widngets there - and each model widget ponts to model which is imported with metadata) When we  test this code we can check the object in memory (console.warn() ) :   Now we can access the metadata using the normal JSON functionality - so we can   access the component data via the Comp Path Id / id paths - corresponds to the modelItem widget property occurrence:         $scope.app.testFunction= function() { // visiting array with string index Object.keys(metaDataArray).forEach(function(key){ console.log(" model Widget = "+ key); let compList = metaDataArray[key]['CompIdList']; //select randomly from the coponent list let randomNum= parseInt(Math.random()*metaDataArray[key]['CompIdList'].length) let randomComp= metaDataArray[key]['CompIdList'][randomNum]; console.log("random component selected = " +randomComp); // let DispName = metadata.get(randomComp, 'Display Name') /*** you can use here one of the following fields "Child Count","Component Name","Display Name","Model Extents (mm)","OL File Name","Part Depth""Part ID","Part ID Path","Part Name","Part Path" ****/ let DispName = metaDataArray[key]['data'][randomComp]['__PV_SystemProperties']['Display Name'] console.log("DispName= "+ DispName) let model_extend_mm = metaDataArray[key]['data'][randomComp]['__PV_SystemProperties']['Model Extents (mm)'] console.log("model_extend_mm= "+ model_extend_mm) let creaDate = metaDataArray[key]['data'][randomComp]['PROE Parameters']['CREATION_DATE'] console.log("creaDate= "+ creaDate) console.log("model_extend_mm= "+ model_extend_mm) let designState = metaDataArray[key]['data'][randomComp]['PROE Parameters']['DESIGN_STATE'] console.log(" designState= "+ designState) // make a selection string for this component let mdl_selection= key+"-"+randomComp; console.log ("mdl_selection="+mdl_selection) // generate some random rgbá color let r =parseInt(Math.random()*255); let g =parseInt(Math.random()*255); let b =parseInt(Math.random()*255); let a =parseInt(Math.random()*0.8)+0.2; //apply blink funciton for this component selection with random color $scope.blinkSelection(mdl_selection,'rgba('+r+','+g+','+b+','+a+')',200,22); }); }         When we test the listed code above we will have in the chrome console window: Using furhter the json  functionality we can also implement e.g. some user picks where you can associate it with the metadata :         angular.forEach($element.find('twx-dt-model'), function(value, key) { // find all model widget feature and perform funciton() // for each model widget - key == modelWidgetId //define the userpick function for each modelWidgetId angular.element(value).scope().$on('userpick',function(event,target,parent,edata) { if (edata) { if ($scope.currentSelection) { // selection is not null make it undefined tml3dRenderer.setColor($scope.currentSelection, undefined); } //create the selection string <modelWidgetId>-<occurance Path Id> $scope.currentSelection = target + '-' + JSON.parse(edata).occurrence; //generate a random rgba color let r =parseInt(Math.random()*255); let g =parseInt(Math.random()*255); let b =parseInt(Math.random()*255); let a =parseInt(Math.random()*0.8)+0.2; //call blinkSelection function for the selected component $scope.blinkSelection($scope.currentSelection,'rgba('+r+','+g+','+b+','+a+')',200,10); //write the data SystemProperty of this compoonent in to a textArea Widget $scope.setWidgetProp('textArea-1','text',JSON.stringify( metaDataArray[target]['data'][JSON.parse(edata).occurrence]['__PV_SystemProperties'])); } }) })           This code will write the meta data to the selected component form the __PV_SystemPoperties section to a text area widget. Also the component will blink fwith random rgba color: I attached an small example (modelMetaDataMobilTest.zip) which should demonstrated the described techniques   Vuforia Studio metadata API The first step is to call the metadata for a model widget. For this we can use the following construct e.g. WidgetId is ‘model-1’ :       PTC.Metadata.fromId('model-1').then( (metadata) => { // <HERE CALL YOUR CODE with metadata > });       So for example we can use get the “Display Name of the component with the path Id =’/0/6’:       PTC.Metadata.fromId('model-1').then( (metadata) => { Let disp_name= metadata.get('/0/6', 'Display Name'); console.log(“Display Name=”+disp_name); });       The most of the  methods which could be applied to the metadata object and also some examples are described in the PTC Help (Incorporate CAD Metadata Into an Experience) This functionality is available first with Vuforia Studio 8.5.13. To this article is also attached a PDF copy of the metioned link above. There we can use 2 different approaches: To get a selected object or list of objects: let metaDATA=PTC.Metadata.fromId('model-1') metaDATA.then(function(meta) { console.log("success func of metaData"); //a test with a fix model widget id var designer = meta.get('/11','DESIGNER','PROE Parameters'); console.log("Designer= " + designer); var ptc_mat = meta.get('/11','PTC_MATERIAL_NAME','PROE Parameters'); console.log("PTC_MATERIAL_NAME= " + ptc_mat); var dispName = meta.get('/11','Display Name','__PV_SystemProperties'); console.log("dispName= " + dispName); var DV_System_Categ= meta.get('/11'). getCategory ('__PV_SystemProperties') console.log(JSON.stringify(DV_System_Categ)) let myQuery=meta.find('Display Name').like('PRT').find('Part Depth').in(0,3); console.log("myQuery Name:"+myQuery._friendlyName); console.log("myQuery selected Paths :"+JSON.stringify(myQuery._selectedPaths)); }) .catch(function(err) {console.log("problem with the read of metadata ");console.warn(err);}); ​ To call for the selected object a callback function where the Path id was passed as function argument:       $scope.app.testCustomSel= function(){ let pathDepth=4 $scope.app.testCustomSelection('model-2', $scope.app.whereFunc,$scope.app.selectFunc,pathDepth) $scope.app.testFind1_modelId='model-1' PTC.Metadata.fromId( $scope.app.testFind1_modelId) .then(function(meta) {meta.find('Part Depth').lessThan(3).find('Display Name') .like('PRT',$scope.app.selectFunc);}) } //========================== app.testCustomSelection $scope.app.testCustomSelection= function(modelId,whereFunc,selectFunc,pathDepth) { BPRN("app.testCustomSelection()"); $scope.app.testCustomSelection.modelId=modelId; $scope.app.testCustomSelection.pathDepth=pathDepth var metaDATA= PTC.Metadata.fromId(modelId) .then(function(meta) {meta.findCustom(whereFunc,selectFunc);}) } //------------------------------------------------- $scope.app.whereFunc = function(idpath) { // scope var `this` is the metadata instance const depth = this.get(idpath, 'Part Depth') const name = this.get(idpath, 'Display Name') return parseFloat(depth) > $scope.app.testCustomSelection.pathDepth || (name && name.search('PRT') >= 0) } //------------------------------------------------- $scope.app.selectFunc = function(idpath) { const name = this.get(idpath, 'Display Name') console.log("app.selectFunc["+$scope.app.testCustomSelection.modelId+"] idpath=" +idpath+ " >>>Name: "+name); return this.get(idpath, 'Display Name');}         In the example above the query is done by the  where function and for all selected models the callback function select Func  is called where the pathId was passed to the function   I attached an small example (modelMetaDataMobilAPItest.zip) which should demonstrated the described techniques
View full tip
Vuforia Studio New Camera widget for mobile and 2D eyewear projects NOTE: This widget is not supported on Windows devices Ability to access and incorporate CAD metadata into an Experience using JavaScript See the Studio Help Center for more information on utilizing this feature New Car Mode checkbox when setting detection configuration properties for a Model Target Bug fixes and minor improvements Vuforia View Support for Car Mode capability for Model Targets Bug fixes and minor improvements Experience Service Support for Windows Server 2019 Support for Red Hat Enterprise Linux 8.0 (RHEL 8.0) Bug fixes and minor improvements
View full tip
Vuforia Studio Bug fixes and minor improvements Vuforia View Bug fixes and minor improvements Experience Service An 8.5.12 version of Experience Service was not released
View full tip
Vuforia Studio Bug fixes and minor improvements Vuforia View Bug fixes and minor improvements Experience Service Compatibility with ThingWorx 9.0 Bug fixes and minor improvements
View full tip
How to define functions to check the different mobile platforms: Checking  if the Preview mode is used: $scope.app.isPreview=function() {// is in Preview if(window.twx.app.isPreview()) return true; return false; } // any other device type  Checking if the device called the Studio project is IOS device: //====================================== $scope.app.isIOS=function() { if(!window.twx.app.isPreview()) // is not in Preview if(ionic.Platform.isIOS()) //and is NOT called on IOS = means Android mode detected return true; return false; } //====================================== Checking if the Studio Project was called on Android device : //====================================== $scope.app.isAndroid=function() { if(!window.twx.app.isPreview()) // is not in Preview if(/android/i.test(navigator.userAgent || navigator.vendor || window.opera) ) // is Android return true; return false; } //====================================== Checking if the Studio Project was called on Windows Phone : //====================================== $scope.app.isWinPhone=function() { if(!window.twx.app.isPreview()) // is not in Preview if(/windows phone/i.test(navigator.userAgent || navigator.vendor || window.opera) ) // is Android return true; return false; } //======================================  or checking if device is a windows system (similiar) //====================================== $scope.app.isWindow=function() { if(!window.twx.app.isPreview()) // is not in Preview if( navigator.platform.indexOf("Win32") >- 1) // is this WindowsPhone return true; return false; } //========================================   The Vuforia Studio is based on cordova so that the most component works for all devices , but in some cases we can use some platfrom specific api. In this case it is important to check on which device the Vuforia app is started to select the platform specific API. Here an example: //======================================== angular.element(document).ready(function () { console.warn($scope) console.warn($rootScope) if($scope.app.isPreview()) { $scope.setWidgetProp('label-3', 'text', "PREVIEW MODE"); twx.app.fn.addSnackbarMessage("PREVIEW MODE","twLogo"); } else if($scope.app.isIOS()){ $scope.setWidgetProp('label-3', 'text', "called on IOS Device"); twx.app.fn.addSnackbarMessage("called on IOS Device","twLogo"); window.onorientationchange = readDeviceOrientation;} else if($scope.app.isAndroid()) { $scope.setWidgetProp('label-3', 'text', "called on Android Device"); twx.app.fn.addSnackbarMessage("called on Android Device","twLogo"); } else if($scope.app.isWindow()) { $scope.setWidgetProp('label-3', 'text', "called on Window Plattform"); alert("called on Windows Phone Device"); } //$scope.setWidgetProp('label-3', 'text', navigator.platform.toString()) if($scope.app.isPreview()) twx.app.fn.addSnackbarMessage("PREVIEW MODE","twLogo"); else { $timeout(twx.app.fn.addSnackbarMessage("navigator.platform="+navigator.platform,"twLogo"),3000); //after 3 sec $timeout(twx.app.fn.addSnackbarMessage("getMobileOperatingSystem()="+getMobileOperatingSystem(),"twLogo"),6000); //after 5 sec $timeout(twx.app.fn.addSnackbarMessage("getPlatform()="+getPlatform(),"twLogo"),9000); //after 5 sec } //=========================================================== angular.element(window).on('resize', function(evt) { console.log("resized window evnt : at Time ::"+$scope.getTime()); console.warn(evt); var message = '' var win = evt.path[0]; if(win.innerWidth > win.innerHeight){ // LANDSCAPE -> do something here call your landscapeFunc() message = "current orientation is Landscape!" $scope.$applyAsync(); } else { // LANDSCAPE -> do something here your PortraitFunc() message = "current orientation is Portrait!" $scope.app.params['myClass']='row3' $scope.$applyAsync(); } twx.app.fn.addSnackbarMessage(message,"twLogo"); });  
View full tip
Getting Orientation of Mobile Device via Javascript and detecting of device rotation on runtime. In some cases it could be a goal to get change the text of a widget based on the mobile devices orientation. This could be done  via Javascript and css. Follwong possible solutions:  In CSS. More details here https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation In Javascript with various method: Here is some documentation : https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation   As we can read, it is not supported in Safari web browser for iOS.   In a new Project, in 2D canevas, I have added one Button and one Label named label-Result. In home.js, we can create this function very similar to the one provided in documentation above.   $scope.screenOrientation = function() { var orientation = screen.msOrientation || (screen.orientation || screen.mozOrientation || {}).type; if (orientation === "landscape-primary" || orientation === "landscape-secondary") { $scope.setWidgetProp("label-Result", "text", "landscape"); console.log("landscape"); } else if (orientation === "portrait-primary" || orientation === "portrait-secondary") { $scope.setWidgetProp("label-Result", "text", "portrait"); console.log("portrait"); } else if (orientation === undefined) { $scope.setWidgetProp("label-Result", "text", "The orientation API isn't supported in this browser"); console.log("The orientation API isn't supported in this browser :("); } } The following observation when we test it :   In Preview, in Chrome web browser, in my workstation in Windows when clicking the button, result is always landscape on Samsung S7 and S9+, in Vuforia View when clicking the button, result is always portrait. Vuforia View doesn't change screen position and rotating the mobile In iPad 6 generation, in Vuforia View when clicking the button, result is The orientation API isn't supported in this browser. Vuforia View screen is changing when rotating tablet So the JavaScript solution above has some problems which we can try to fix using another approach: As mentioned the previous solution could be used only on start- but it will not detect a dyncamic change - e.g. rotate the device.This solution  was tested and working in preview and on android. It seems that it does not work on IOS.  ON HoloLens we have  only landscape mode- so that this has no relvance. For Preview mode and Android devices the  following code is working fine: //============================================ angular.element(document).ready(function() { angular.element(window).on('resize', function(evt) { //console.log("resized window evnt :");console.warn(evt); var message = '' var win = evt.path[0]; if(win.innerWidth > win.innerHeight){ // LANDSCAPE -> do something here call your landscapeFunc() message = "current orientation is Landscape!" } else { // PORTRAIT -> do something here your PortraitFunc() message = "current orientation is Portrait!" } twx.app.fn.addSnackbarMessage(message,"twLogo"); }); }) //////////////////////  We need to pay attion here that we are in angular js environment and  the windows variable seems to  be static an is passed on system start - therefore it will not update dynamicaly. There is also an soluton for IOS which was verfied in a tests - e.g.  it works fine (IPad 6 generation).  Possibly this solution will work also on window -need to be testet. Following code: function readDeviceOrientation() { //only for IOS var orient="unknown" switch (window.orientation) { case 0: orient= "Portrait" break; case 180: orient= "Portrait Upside-down"// Portrait (Upside-down) break; case -90: orient= "Landscape (Clockwise)"// Landscape (Clockwise) break; case 90: orient= "Landscape (Counterclockwise)"// Landscape (Clockwise)// Landscape (Counterclockwise) break; } twx.app.fn.addSnackbarMessage("IOS Orientation: "+orient,"twLogo"); } And register the event on orientation change: angular.element(document).ready(function () { if($scope.app.isIOS()){ $scope.setWidgetProp('label-3', 'text', "called on IOS Device"); twx.app.fn.addSnackbarMessage("called on IOS Device","twLogo"); window.onorientationchange = readDeviceOrientation;} } }); Here is a function for testing of the current device -> please, check for more details this post( How to define functions to check the different mobile platforms ). So, to go deeper, we can see this thread in stackoverflow.com : https://stackoverflow.com/questions/4917664/detect-viewport-orientation-if-orientation-is-portrait-d... IOS Reference: http://www.williammalone.com/articles/html5-javascript-ios-orientation/#:~:text=The%20JavaScript%3A,%3D%20%22LANDSCAPE%20%22%20%2B%20window.
View full tip
Vuforia Studio Bug fixes and minor improvements Vuforia View Improved detection and tracking for Model Targets and Spatial Targets (all platforms) HoloLens 2: Improved QR code scanning performance Bug fixes and minor improvements Experience Service Bug fix for the Trust Proxy setting Other bug fixes and minor improvements
View full tip
How to define Widget at runtime time and what is possible?  For example, the following problem: required is a button e.g. 'button-1' - widget to start a sequence. Now user want to create an 3D-Label that becomes visible after click the 'button-1' and  now for each sequence and step a the 3D-Label have to be visible as long as the sequence is played. The 3d label should display an information for the specific step and also should to have specific position. Unfortunately, it is not possible (using the supported functionality) to create widget on the fly - means create a new feature on run time where the widget was not defined in the design time. Theoretical with some more intensive work – it will be possible – we simple need to check what the UI is doing when you copy and paste widget  (but it is not easy ☹-  and it is not sure if this solution will be stable(if you will be able to implement 1:1 UI) and if it will work in later version -because PTC dev team could change some functionality – but will not change your code. For the most tasks it enough to use few 3d widgets (3Dlabels) the most are invisible and then switch the visibility on runtime and move them on the desired location. According request on address of this issue  , may be it is worth here to mention following statements of the PTC dev  :   .) Question: “Vuforia Studio 8.5.3.Is it possible to create 2D and 3D widgets from Java Script? Answer: If you mean to dynamically create/instatiate a widget and set its properties at runtime, no we have no such capability today. The simplest workaround is to pre-generate the widgets and manage their visibility at runtime. 2.) Question: Is it possible to create 2D widget dynamically by Java Script? Answer: Its fairly easy to have a couple of hidden widgets that are displayed and moved with a tap event. It might be possible to create some 2d widgets on the fly, but 3d widgets would be a bit harder as more of the unsupported api under the hood would be needed.   According to the statement of the PTC development team  - here the we will consider the possible workaround :. The following requirement: When the sequence 1 is played the labels 1, 2 and 3 have to be visible. When the sequence 2 is played the labels 4, 5 and 6 have to be visible. In all of these labels are only one word in it, so not a step description or something like this. Here is a sample table with coordinates for a better overview:   There is no a real reason to have 4,5,6 here. In this case we can use 1,2,3 again - also for step 2 but we could here change the position and will change the text content. In this case we need to have to define number of 3DLabel widgets which is equal to the maximum of used notes per step - and display and update only the necessary number of widget in particular step.   To demonstrate the workaround the following example was created and tested. This   example should show the suggestion above. It is not perfect but should demonstrate the general approach to achieve similar goal. The table with values is a json file- in the attached example is the file steps.json in the Project upload folder. Here example of values: {"1":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 1", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel1"}, "3DLabel-2":{"visible":true,"text":"text Label2 Step 1", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}, "3DLabel-3":{"visible":true,"text":"text Label3 Step 1", "x":0.0,"y":0.3,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"}}, "2":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 2", "x":0.0,"y":0.1,"z":0.1,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}, "3DLabel-2":{"visible":false,"text":"text Label2 Step 2", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}, "3DLabel-3":{"visible":true,"text":"text Label3 Step 2", "x":0.0,"y":0.3,"z":0.1,"rx":0.0,"ry":10.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}}, "3":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 3", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":20.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"}, .... "8":{"3DLabel-1":{"visible":true,"text":"text Label1 Step 8", "x":0.0,"y":0.1,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel1"}, "3DLabel-2":{"visible":true,"text":"text Label2 Step 8", "x":0.0,"y":0.2,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel2"}, "3DLabel-3":{"visible":true,"text":"text Label3 Step 8", "x":0.0,"y":0.3,"z":0.0,"rx":0.0,"ry":0.0,"rz":0.0,"scale":1.0,"class":"ptc-3DLabel3"}}}      The table contains the most possible values to set for 3DLabels. We can add other or omit values in the list. In generally we need only the values which should be changed but such list template which contains all values is better for editing. It should still work so far, the json syntax is correct First point is to load the list to a global variable from the project upload folder. Here a sample code:   $scope.jsonData_steps={}; //global $scope variable //================================== readSteps=function (jsonFile){ console.warn("**=>readSteps :: "+jsonFile); fetch(jsonFile) .then(response=>response.text()) .then(data=>{$scope.jsonData_steps=JSON.parse(data); console.warn( JSON.stringify($scope.jsonData_steps))}) .catch((wrong) => {console.log("problem in fetch: "); console.warn(wrong)}) } //================================== $scope.Init=function() { $timeout(readSteps('app/resources/Uploaded/'+stepsjson),200); } //================================================================================================= // $ionicView.afterEnter -> this event fires when 2d view was entered //================================================================================================= $scope.$on('$ionicView.afterEnter',function(){ console.log("$ionicView.afterEnter was called"); $scope.Init();}) //event: when 2d View loaded //=================================================================================================     further in the "stepstarted" event the code will set the values of the widget properties which are contained by the json object  with the same number as the started step number:     //================================================================================================= $scope.$on('stepstarted', function(evt, arg1, arg2, arg3) { var parsedArg3 = JSON.parse(arg3); console.log("stepstarted stepNumber="+parsedArg3.stepNumber + " nextStep="+parsedArg3.nextStep); $timeout(()=>{ $scope.setMutipleProps($scope.jsonData_steps[parsedArg3.stepNumber.toString()])},10) $scope.setWidgetProp('label-1', 'text',"STEP: "+ parsedArg3.stepNumber) $scope.$applyAsync(); }); //================================================================================================= //this function set multiply properties from a list //================================================================================================= $scope.setMutipleProps = function(obj){ Object.keys(obj).forEach(function (item) { for(var prop in obj[item]) { $scope.view.wdg[item][prop]=obj[item][prop]; //print the setting for debugging console.log("==>$scope.view.wdg["+item+"]["+prop+"]="+obj[item][prop] ) } }) $scope.$applyAsync(); }; ///=================================================================================================     Finally,  the project was  tested  and  it was working as expected:     The demo project is attached to this article.
View full tip
Sometimes it is required and will be nice to use a picker functionality. For example, some data picker – so the question: How to achieve this. Yes it is possible in JavaScript that we can incorporate a data/calendar picker into the Vuforia Studio environment? In the Web there are some open source libraries and at least it works 1:1 in preview mode. But mostly they work also fine on mobile devices. In this article a data picker was tested and it was working fine in Preview mode but also was working fine on IOS and on Android devices  The example here is based on the follow link:  https://www.cssscript.com/tag/date-picker/ there are some data picker implemented. Here in this example the library (data-picker) is copied to  the Studio Project download folder and it javascript code will   load the lib and defined a function which is called from button First step is to define a function which enables to load javascript and css from a file / project folder:   // this code load a javascript or css file $scope.loadjscssfile= function(filename, filetype){ console.log("loading "+filename+":: type="+filetype) if (filetype=="js"){ //if filename is a external JavaScript file var fileref=document.createElement('script') fileref.setAttribute("type","text/javascript") fileref.setAttribute("src", filename) } else if (filetype=="css"){ //if filename is an external CSS file var fileref=document.createElement("link") fileref.setAttribute("rel", "stylesheet") fileref.setAttribute("type", "text/css") fileref.setAttribute("href", filename) } if (typeof fileref!="undefined") document.getElementsByTagName("head")[0].appendChild(fileref) } // this function will load the simplepicker javascript lib and the css lib $scope.testLoad= function() { $scope.loadjscssfile("app/resources/Uploaded/lib/simplepicker.css", "css") $scope.loadjscssfile("app/resources/Uploaded/dist/simplepicker.js", "js") }   In this example the $inoicView.afterEnter (after 2d/view  is loaded) event is used to load the library. The function for the picker call is defined -  $scope.app.TestPicker()- which is called from a button .   //============================= $scope.$on('$ionicView.afterEnter', function() { $scope.testLoad(); $timeout(() => { $scope.setWidgetProp("button-1","class", "simplepicker-btn"); $scope.$applyAsync(); },500) }) //https://www.cssscript.com/material-date-time-picker-simplepicker/ // function called from a button $scope.app.TestPicker = function() { let simplepicker = new SimplePicker({ zIndex: 10 }); simplepicker.open(); simplepicker.on('submit', (date, readableDate) => { console.warn( readableDate ); $scope.setWidgetProp('label-1','text', readableDate); $scope.$applyAsync(); }); simplepicker.on('close', (date) => { console.log('Picker Closed' ); }); }   When the picker is closed after date was selected - the javascript code will set the selected value to the text property of a label widget (label-2). The javascript and the style (css)  implementation was copied to the resource folder:       Now the   simplepicker could be tested and it has the following appearance in preview mode:     The test picker Studio project was attached to this case.  
View full tip