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

Community Tip - You can subscribe to a forum, label or individual post and receive email notifications when someone posts a new topic or reply. Learn more! X

Vuforia Studio and Chalk Tech Tips

Sort by:
With various Augmented Reality applications in PTC's product portfolio the technical aspects and use cases could leave you with some questions. Did you know that we do not only have a full blown Augmented Reality SDK but offer also the possibility for a easy to use integration with live sensor data coming in via ThingWorx?   This blog post hopefully clarifies some of the questions around what can be done with Vuforia SDK and Vuforia Studio.   Welcome to the real world   In the real world, or the "real reality" (sounds weird, but it's basically what you can see with your own eyes - no augmentation involved) there are various objects. These might look the same - or not. Just take the following example... that's what we perceive when looking at things around us:     These objects are recognized​ via shapes, contrasts (black & white) and whatever defines the actual form. ​Vuforia SDK ​is able to recognize those objects via it's built-in object recognition capabilities. However, there might be limits - depending on the use cases...   While buildings could be distinguished by their form, playing cards could be distinguished via their suits and nominations. The machines however, they all look the same, they probably all ​are​ the same.   Combining the real world with a virtual world   "Augmented Reality" will allow to enhance this physical object with virtual properties, e.g. overlay its CAD-Model or overlay some animations for a better gaming experience. Check out this video for the Genesis Augmented Reality Trading Card Game example.   Object Recognition allows to put actual names to what the (digital) eye can see:     Once the object is recognized and identified all kinds of virtual attributes can be added. Vuforia SDK allows to do this with e.g. Unity.   As all of the machines are basically the same... they look the same, come from the same manufacturer and behave the same, ​identification​ can only be done via a manual effort, e.g. selecting the actual machine manually within an app (via a menu etc.). This manual selection process will then map a generic form and shape of the machine to the actual physical machine you can see and touch just in front of you.   In an app this might be necessary if you can recognize the generic form of a playing card but forgot to implement the suit and nominations. In that case, either extend the recognition part, or choose a drop-down list when the card is identified to choose the actual​ card in front of you.   How do ThingMarks fit in?   Using the functionality of Vuforia SDK, Vuforia Studio combines the power of Vuforia (AR) with the power of ThingWorx (live sensor data / object information). In an industry environment I could select the correct machine I'm looking at. However, what's the identifier? It is probably written somewhere on the back of the machine with lots of other information, so I don't really know what to look for. Therefore I could be looking at any machine, but without the identifier I can not retrieve information for ​my​ machine.   Vuforia Studio uses ThingMarks​. They work similar to a QR-Code and allow for direct identification of individual machines. So instead of choosing manually in the app, the ThingMark automatically chooses the correct object and relates that ID to a Thing Entity in ThingWorx.     In above image, the ThingMark allows to a) identify we're looking at a machine and b) are looking at the specific machine A03 It's basic point and shoot. Scan the ThingMark with your mobile device and you're directly taken to this particular experience for this particular machine.   In this case, it's not the machine that defines our object's properties and shapes and contrasts and sizes etc. In this case, it's the ThingMark that's the object being recognized. That's quite a difference.   So now, in an additional step, we're using the power of Vuforia to identify individual machines by a ThingMark. Recognition is driven by the ThingMark's shape which includes an encoded object ID (the QR-code looking pattern).   How does ThingWorx fit in?   After recognizing the machine, ThingWorx studio provides the link between this specific object (or its instance) and the ThingWorx Thing Entity we've defined in Vuforia Studio.   This allows to retrieve individual properties, services, events, alerts etc. directly via ThingWorx. Those values are unique per object, not per shape!   So this allows to directly look at temperature, level and failure-indicator for the actual machine in front of us:     Bridging the gap   Vuforia Studio​ is used to bridge the gap between ​Vuforia ​and its Augmented Reality capabilites as well as ThingWorx ​and its Internet of Things (IoT) capabilities. Vuforia Studio uses parts of both applications, adds own functionality and defines its own product category: Connected Augmented Reality​     There are quite some components involved in this:     This can be split into two processes: developing and experiencing   Development   Create a new experience in VuforiaStudio, map the experience to the ThingMark ID, map the experience to a Thing Entity in ThingWorx. Publish the experience to the Experience Server. Done.   Experience   Scan the ThingMark with the Vuforia View app. Vuforia View will utilize Vuforia to recognize the ThingMark Vuforia View will load the data and the model(s) for this ThingMark from the Experience Server Vuforia View will automatically receive and update the experience you're viewing with live data from the ThingWorx platform Enjoy.   Resources   There are quite some videos, tutorial, best practices etc. available on how to develop and experience the world of Vuforia Studio. Check out ThingWorx Studio Resources: Getting Started Guides, Tutorials, Troubleshooting for the Article Hub and quite a lot of good stuff!   More information   To get more information visit the product pages at https://www.vuforia.com https://trial.studio.vuforia.com/   If you're looking for help, these might be of interest:   https://developer.vuforia.com/support for Vuforia SDK https://community.ptc.com/t5/Studio/bd-p/studio for Vuforia Studio https://community.ptc.com/t5/ThingWorx-Developers/bd-p/twxdevs for ThingWorx https://support.ptc.com/    What's next?   Get involved, create your own experience. It's fun, it's quite easy and well... it looks good, too!  
View full tip
Using Connection Filters for state-based rendering and value formatting   State-based rendering of properties that come from user inputs or from connected ThingWorx data is a common requirement. Think about the following scenario: from ThingWorx you get the state of a as one of these values: [ OK | At-Risk | Issue ]. You would like to visualize it with a 3D image or a changed 3DGauge icon.   You can achieve that easily with a Vuforia Studio functionality that is often overseen: The Connection Filters. With connection filters you can achieve a lot of very nice a common features, mainly: number and date formatting value pre- and postfixing (specifically units for numbers but also anything else) state-based formatting, e.g. changing the image or style based on a number change   Here is what I mean in practice:   Step 1:   Step 2:   Here is the stateBasedStyling Connection Filter content:   return value < 3000 ?    "fill:rgba(255, 255, 255, 1);textbaseline:middle;textalign:center" :    "fill:rgba(255,   0,   0, 1);textbaseline:middle;textalign:center" ;   value is a key word and represents the value on the connection before applying the filter function. The returned value is the one that is used in the bound-to attribute. I found that the expression has to be a one-liner (technically), i.e. you have to start your expression with 'return' and can't do something like:   if(value < 3000) return "fill:rgba(255, 255, 255, 1);textbaseline:middle;textalign:center"; else return "fill:rgba(255,   0,   0, 1);textbaseline:middle;textalign:center";     Resulting Filter in ThingWorx Studio: You can add multiple filters but I didn't check yet in which sequence they'll be evaluated (top-to-bottom or bottom-to-top).     Examples of Usage   1. Show icons for Enum values: I used it often to display enumerated-type values (i.e. the input values are from a discrete value set e.g.named colors) with images/icons:   return "app/resources/Uploaded/colorImg_"+value+".png";   in combination with the corresponding uploaded images, in this case named colorImg_red.png, colorImg_blue.png, etc.   2. Format Numbers and postfix with unit The following formats the input number 12.769 to 12.8 mph.   return $filter('number')(value, 1) + " mph";     See the AngularJS documentation for the available filter types. Relevant are: currency number date   I haven't tested yet if these filters can also be applied to input arrays of objects that come from a service (e.g. for a repeater widget). If this is supported the array-based filters may be applicable/usable as well.  
View full tip
If the experience project exists in Vuforia Studio Unpublish the project by hovering over the project and clicking the unpublish project Experiences icon . This action removes Experiences from the Experience Service. If the experience project does not exist in Vuforia Studio Using CURL Command Curl -u <username>:<password> -H "Content-Type: Application/JSON" -X "DELETE" https://<your-domain-name>/ExperienceService/content/projects/<projectname> username: Experience Service username password: Experience Service password your-domain-name: Experience Service domain projectname: Experience project name to be deleted Using REST call from Postman Select query method as 'DELETE' Enter the URL as https://​<your-domain-name>/ExperienceService/content/projects/<project-name> your-domain-name: Experience Service domain projectname: Experience project name to be deleted In Authorization menu Choose Authorization type as 'Basic Auth'. Add the user credentials and update request.  
View full tip
Your company might have a css that represents the corporate identity - or you may have other sources of reusable css styles that you want to include with minimal effort. Here is what you have to do to use corporate css files to drive the look and feel of your experiences: Add the corporate css file (e.g. company.css) to your resources In Application styles add the following at the beginning (before any other css style entry: @import url(#{$resources}/Uploaded/company.css);     With the following content in company.css: And this label definition:   Produces this outcome (you see it in the editor as well as in the preview):   Gotcha!    
View full tip
Just helped someone who was seeing a difference between the Preview mode and what Vuforia View was showing. The experience was being developed for public access, so what was happening is that Preview mode worked because they were using their Studio credentials to run the Thingworx service. When they used Vuforia View, they were using es-public-access to run the Thingworx service. And es-public-access did not have the runtime permissions to execute the service.   The error indication was found in the Application Log complaining about the Entity and Service not being able to be run.   Don't forget to set your permissions for es-public-access so it can run your services!  
View full tip
With release 1.9.1, pilot and free trial participants can auto-configure Vuforia Studio to make it easier to get up and running quickly.  The auto-configure process does the following:​ Configures the sample projects included with your Vuforia Studio installation so that when you publish those projects they are published to your experience service and can be viewed in Vuforia View using one of your ThingMarks Retrieves the Experience Service (ES) URL - can find at Project -> Configuration -> Info section. We are no longer sending the ES url through welcome email. Downloads your ThingMarks and makes them available on the My ThingMarks page inside Vuforia Studio so that you can view your ThingMarks and print them out In order to complete the auto-configuration process, users are first required to authenticate using their PTC Account credentials.  For participants in the Vuforia Studio Free Trial, this does not introduce any confusion since they use their PTC Account credentials for everything: accessing the Studio Portal, publishing experiences from Vuforia Studio, downloading experiences to Vuforia View and working in ThingWorx Composer.   However, for participants in the Vuforia Studio Pilot Program, this may introduce some confusion.  Unlike free trial participants, pilot participants have two sets of credentials: PTC Account credentials used to access the Studio Portal and Auto-Configure Vuforia Studio Experience Service credentials provided in their Pilot Program Welcome Email that are used to publish experiences from Vuforia Studio, view experiences in Vuforia View and access ThingWorx Composer The auto-configuration process requires users to authenticate using their PTC Account credentials.  Since the auto-configuration process occurs inside Vuforia Studio and pilot participants do not normally use their PTC Account credentials inside Vuforia Studio, this may cause some confusion.   Note   The users that received access to an experience service instance before February 17, 2017 is a participant in the pilot program Any user that received access to an experience service on or after February 17, 2017 is a participant in the free trial.  
View full tip
This is the third JavaScript quark in the series: it can be used to change a widget color by cycling through a given array of colors. You can find the second quark here.   Here's the code to copy & paste to your Home.js:   $scope.cycleColors = function(widget, colors, time, interval) {   let w = (widget.color !== undefined ? widget : $scope.view.wdg[widget]);   if (!w || w.color === undefined) { throw "Cannot color-cycle this widget"; }   let originalColor = w.color;   w.color = colors[0];   w.visible = true;   w.opacity = 1;    let nSteps = Math.ceil(Math.floor(time/interval) / colors.length) * colors.length;   return $interval(iterationCount => w.color = iterationCount === nSteps ? originalColor : colors[iterationCount % colors.length], interval, nSteps); } This JavaScript quark will make the widget color cycle through the colors provided in the colors array. The effect will last time milliseconds, and each color change will happen every interval milliseconds.   Invoke the function like this:   cycleColors(widget, colors, time, interval);   where widget is either the id of the widget (e.g. modelItem-1) or the widget itself (e.g. $scope.view.wdg['modelItem-1']), colors represents an array of colors (e.g. ["rgba(200,0,0,1)", "rgba(0,0,200,1)"]), time represents the total time in milliseconds it takes to execute this effect, and interval represents the amount of time in milliseconds between each color change.   Here's an example:   cycleColors("modelItem-1", ["rgba(200,0,0,1)", "rgba(0,200,0,1)", "rgba(0,0,200,1)"], 2000, 50); This example cycles the model item color through red, green and blue; the effect will last for 2 seconds with a color change every 50 ms.   Comments and suggestions are welcome.   -Alessio  
View full tip
This is the second Javascript quark in the series: it can be used to fade a widget out. You can find the first quark here.   Here's the code to copy & paste to your Home.js:   $scope.fadeOut = function(widget, time, interval) { let w = (widget.opacity !== undefined ? widget : $scope.view.wdg[widget]); if (time <= 0 || interval <= 0 || w.opacity === undefined) { throw "Cannot fade this widget"; } let steps = Math.floor(time / interval); let opDelta = w.opacity / steps; return $interval(() => w.opacity = (opDelta < w.opacity) ? (w.opacity - opDelta) : 0, interval, steps); } This quark will make the widget fade out from its current opacity to 0 in time milliseconds, uniformly decrementing opacity at every interval .   Invoke the function like this:   fadeOut(widget, time, interval);   where widget is either the id of the widget (e.g. modelItem-1) or the widget itself (e.g. $scope.view.wdg['modelItem-1']), time represent the total time it takes to fade the widget out from its current opacity, and interval represents the amount of time between each opacity change.   Here's an example:   fadeOut('modelItem-1', 2000, 50); This example fades the model item out by bringing its current opacity to zero after 2 seconds with an opacity change every 50 ms.   Comments and suggestions are welcome.   -Alessio    
View full tip
Hi,   we can do many tricks with Javascript in Studio and most of the times it's just a matter of copying & pasting the right code.   I'd like all Studio users, not just coders, to benefit from this, and thought I could drop here a snippet to blink a widget.    I call this a quark - from the particle physics standard model - and not atom, because it's really a smaller building block than an atom    Blinking a widget can be useful, for example, if you are not using Creo Illustrate to create a sequence but still want to draw the user attention to some item in the scene.     Here's the Javascript code to copy & paste to your Home.js:  (to Javascript coders: I'm using modern Javascript syntax, don't be frightened by that )   $scope.blink = function(widget, times, interval) { let w = (widget.visible !== undefined ? widget : $scope.view.wdg[widget]); if (!w || w.visible === undefined) { throw "Cannot blink this widget"; } $interval(() => w.visible = !w.visible, interval, times); }    Invoke the function like this:   blink(widget, times, delay); where widget is either the id of the widget (e.g. modelItem-1) or the widget itself (e.g. $scope.view.wdg['modelItem-1']). The other two numbers are the number of times that you want visibility to change, and the amount of milliseconds between each visibility change.     Here follow some examples.   You want to blink the widget 4 times with a 300 ms interval (and leave the widget visible at the end): blink('modelItem-1', 2*4, 300);   You want to blink the widget 4 times with a 300 ms interval (and leave the widget not visible at the end): blink('modelItem-1', 2*4+1, 300);   You can comment and suggest additional quarks if you want.   Alessio  
View full tip
Often issues connecting to the Thingworx Experience Service from Thingworx Studio are related to more complex proxy configurations. One such configuration uses a .pac script that dynamically resolves the proxy based on the requested URL. Thingworx Studio has the ability to configure a proxy server but you have to explicitly specify one server URL, you can't replicate the settings in your internet connections when these use a .pac script. No worries - there is a workaround - Proxy-Vole at your rescue!   You can find Proxy Vole (https://proxy-vole.kenai.com/ ) on the Internet. It is a little java-based application that can be used to auto-resolve your proxy configuration. It has a command line and a UI frontend. The documentation is somewhat unstructured - for the test you only need a few of the steps: Download the Proxy Vole jar-with-dependencies Start the proxy vole application in a command shell using the following command: java -cp ./proxy-vole-1.0.1-jar-with-dependencies.jar com.github.markusbernhardt.proxy.ui.ProxyTester Enter the following in the dialog box: 4. Specify the resulting proxy URL in Thingworx Studio: That's it! If you still have issues, please post the log on the Developer Forum site.  
View full tip
You can get the view count for all public experiences by using the below REST API call.   GET <your ES URL>/compliance/views   This will return a JSON object with a views and billables field where: views = the download number of publicly accessible projects & billables = how many downloads are counted towards billable tokens   You can also specify the startDate and endDate in your request. These parameters can be defined by a UTC date string, JSON date string, or time in milliseconds since a specified date.   
View full tip
Unfortunately, in the Vuforia Studio Documentation there is no complete List with the possible events which could be handled JS. Therefore for the first time this article tries to provide additional Information about known events :   1.) modelLoaded - is not required any more because the UI allow directly to specify this event. ... $rootScope.$on('modelLoaded', function() { //do some code here } ) .... 2.) Step completed example: scope.$on('stepcompleted', function(evt, arg1, arg2, arg3) { var parsedArg3 = JSON.parse(arg3); console.log("stepcompleted stepNumber="+parsedArg3.stepNumber + " nextStep="+parsedArg3.nextStep); $scope.app.stepNumber=parseInt(parsedArg3.stepNumber); $scope.app.nextStep=parseInt(parsedArg3.nextStep); $scope.app.duration=parseFloat(parsedArg3.duration); }); 3.) Event - stepstarted: ... $scope.$on('stepstarted', function(evt, arg1, arg2, arg3) { var parsedArg3 = JSON.parse(arg3); console.warn(arg3); console.log("stepstarted stepNumber="+parsedArg3.stepNumber); $scope.app.stepNumber=parseInt(parsedArg3.stepNumber); $scope.app.nextStep=parseInt(parsedArg3.nextStep); $scope.app.duration=parseFloat(parsedArg3.duration); }); ... Please, pay attention that on some platforms will not provide complete information  in stepstarted. So, In this case the complete info is available in 'stepcompleted' – the best is to test it.   4.) after entering in  a view in studio (e.g. Home ...):   ... $scope.$on('$ionicView.afterEnter', function() {$scope.populateModelList(); }); ... 5.) click/tap event on the current panel: ... $rootScope.$on('click', function() { tapCount++;console.log("click event called");} ); ... or  with coordinates ... document.addEventListener('click', function(event) {console.log("click() 1 called"); $scope.lastClick = { x: event.pageX, y: event.pageY}; }); ... you can also see this topic.   6.) New step  -example: ... $scope.$on('newStep', function(evt,arg) { var getStepRegex = /\((\d*)\//; console.log(arg); console.log( getStepRegex.exec(arg)[1]); //check what it prints to the console - the step number }); ...    7.) Here is also  a more advance construct- it defines a userpick event e.g. for all models widgets: angular.forEach($element.find('twx-dt-model'), function(value, key) { // search all twx-td-model's -> means all model widgets angular.element(value).scope().$on('userpick',function(event,target,parent,edata) { //for each model widget will set a userpick listener console.log('edata');console.warn(edata); console.log("JSON.parse(edata)");console.warn(JSON.parse(edata)); var pathid = JSON.parse(edata).occurrence; $scope.currentSelection = target + "-" + pathid; // this is the current selection - the selected component occurence // you can use it for example as shown below // try{ //tml3dRenderer.GetObject($scope.currentSelection).GetWidget().ApplyOccludeOpacity(OCLUDE_VAL,OPACITY_VAL); //} catch (e1234) {$scope.view.wdg['3DLabel-4']['text']= "e 1234exception in GetObject.GetWidget..."; } // } ) //end of the userpick defintion } ) //end of for each funciton  8.) tracking event:   ... $scope.$on('trackingacquired', function (evt,arg) { // alert('didStartTracking'); // this is not really needed $scope.message = parseInt($scope.app.params["currentStep"]); $scope.$apply(); }); $scope.$on('trackinglost', function (evt,arg) { // alert('didFinishTracking'); $scope.message = "Scan the ThingCode with your camera."; $scope.$apply(); }); ....   9.) popover event:     // var my_tmp = '<ion-popover-view><ion-header-bar> <h1 class="title">My Popover Title</h1> </ion-header-bar> <ion-content> My message here! </ion-content></ion-popoverview>'; $scope.popover= $ionicPopover.fromTemplate(my_tmp, { scope: $scope }); $ionicPopover.fromTemplateUrl('my-popover.html', { scope: $scope }).then(function(popover) { $scope.popover= popover; }); $scope.openPopover= function($event) { $scope.popover.show($event); }; $scope.closePopover= function() { $scope.popover.hide(); } //////////////destroy popover $scope.$on('$destroy', function() { $scope.popover.remove(); }); /////// hide popover $scope.$on('popover.hidden', function() { // your hide action.. }); // on remove popover $scope.$on('popover.removed', function() { // your remove action }); }); 10) watch event -watches are created using the $scope.$watch() function. When you register a watch you pass two functions as parameters to the $watch() function: 1)A value function 2)A listener function    When the value returned by function 1.) changes - this lead to execution of the funciton 2.) Example:   ... $scope.$watch(function(scope) { return $scope.view.wdg['label-1']['text'] }, // watches if change for the the text of label-1 //when changes then play a step for model-1 function() { console.log($scope.view.wdg["model-1"]); $scope.view.wdg["model-1"].svc.play; } ); ...   11.) Camera tracking - make sense only on mobile device- no sense for preview mode!   //// define tracingEvent only on end device tml3dRenderer.setupTrackingEventsCommand (function(target,eyepos,eyedir,eyeup) { // $scope.view.wdg['3DLabel-1']['text']="eyepos=("+eyepos[0].toFixed(2)+","+eyepos[1].toFixed(2)+","+eyepos[2].toFixed(2)+")"; $scope.app.params['target']=target; $scope.app.params['eyepos']="eyepos=("+eyepos[0].toFixed(2)+","+eyepos[1].toFixed(2)+","+eyepos[2].toFixed(2)+")"; $scope.app.params['eyedir']="eyedir=("+eyedir[0].toFixed(2)+","+eyedir[1].toFixed(2)+","+eyedir[2].toFixed(2)+")"; $scope.app.params['eyeup'] ="eyeup =("+ eyeup[0].toFixed(2)+","+ eyeup[1].toFixed(2)+","+ eyeup[2].toFixed(2)+")"; ///////////////////// },undefined); //// define tracingEvent only on end device } //end device   12.) There is also a sequenceloaded event, which is useful if you have a model with multiple sequences defined, and you are switching sequences dynamically in the experience.   $scope.$on("sequenceloaded", function (evt, arg) { console.log("sequence loaded, starting play"); $scope.setWidgetProp("loading","visible",false); $scope.app.fn.triggerWidgetService("model-1","playAll"); }); In this point is  here a good feedback comming from advance user (expert) : If you grep for "$emit" through a project folder, you can turn up the following event names: valueacquired (bar code scanner) usercanceled (bar code scanner) tracking modelloadfailed sequenceloaded newStep playstarted sequenceacknowledge playstopped sequencereset onReset If you grep for "$on(", you can find some additional ones: trackingacquired trackinglost modelLoaded click app-fn-navigate app-fn-show-modal app-fn-hide-modal $ionicView.afterEnter $stateChangeStart loaded3DObj loadedSeqErr loadedSeq $destroy select3DObj move3DObj loadError3DObj readyForZoom3DObj serviceinvoke stepstarted stepcompleted twx-entity twx-service-input twx-service-name This is a  good point and it seems that this list contains the most of the possible events.   Events Handling Feedbacks from EXTERNAL DATA  services  Such event is    the twx-service complete event. This event is called when we call a service registered in the external data and the service is completed. Here an example (also mention in the post😞 Here in the example in the external data the service LoadJSON was added.       ////////////////////////////////////////////////////////////// $scope.GetJsonFromTwxRepository = function(path) { $scope.$applyAsync(function() { $rootScope.$broadcast('app.mdl.CAD-Files-Repository.svc.LoadJSON', {"path":path} );} ,500 ); $scope.app.speak("after call of GetJsonFromTwxRepository") //in the modelloaded listener register // LoadJSON-complete event -> to laod the data into session rootScope.$on('modelLoaded', function() { //// $scope.$root.$on('LoadJSON-complete', function(event, args) { console.log("LoadJSON-complete event"); $scope.COMP_LOCs=args.data console.log(JSON.stringify( $scope.COMP_LOCs)) }); /// });   So the code above shows how to call from JavaScript the service added to the external data. This service should return the called JsonObject. The call is asynchronously so that when  thingworx  will come back the listener 'LoadJSON-complete' will be called and here will print the content of the JsonObject to the console. Here the listener is registered inside the modelload event (this is event is coming late – so to be on the save side that everything is already initialized) This is generally that you for any Thingworx services added to the External data <your_twx_service_name>-complete  the arg.data contains then the data which should be returned by the method.
View full tip
Announcements