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:
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
In the post “How to select model components in a 3d model without explicit definition of model Items”  there is one point where we  require a list of component items. Often we have the  case where we get a Creo View .pvz file which was published by Creo Illustrate.  In this case Creo Illustrate will  do additional changes for  the occurrence Id path  so that if we have a bom list coming from Creo Parametric then this data  will be not usable. So the question here is :  Is it possible to extract a bom list for any *.pvz files? The answer is Yes.  We can do this  using the Creo View Toolkit API ( this is one possible option). Creo View API Toolkit consist of many different moduls : Java Toolkit , Web Toolkit (only Internet Explorer related) Office Toolkit and  the new one introduced since 5.1 release module WebGL Toolkit.  We can extract BOM list  with Java Toolkit but it requires more complex programming environment. Therefore I think it is the most easily way to use Creo View WebGL toolkit   The Toolkit WebGl is based on the Thingview.js library. When we install Creo View Toolkit we will find the following directory structure:     In the web-application-examples  directory we can find some sample files which could be a good introduction how to use this API. The documentation is the WebGLToolkitDevGuide_en.pdf  and also in html(doxygen) in the documentation sub directory:     The CreoWebGL Toolkit requires a node.js environment. So  when we want to start it we have first to start the Toolkit server: >>>node http_server.js [port]   The port parameter is optional and if we do not use it then it takes by default 8080 :     The next step is to open the localhost:port/ ExtractBomPVZ.html which implements  here our program For example a simple version which will print the components with some properties  to the chrome console  could  looks like:   <!DOCTYPE html> <html style="height: 100%"> <head> <meta charset="utf-8" /> <meta name="author" content="PTC"> <title>Creo View WebGL Viewer</title> </head> <script src="js/ptc/thingview/thingview.js"></script> <body style="height: 100%; margin: 0px"> <div id="TITLE" style="width: 100%;height: 20%;border:2; float: left"></div> <div id="CreoViewWebGLDiv" style="width: 100%;height: 80%;border:0; float: left"></div> </body> <script type="text/javascript"> var thingview; var session; var model; var model1; var model2; var MODELPATH = "sample-data/thingview_test/Machine-complete-CV.PVZ"; var MODELNAME = ""; var global_number_generated = false; var BOM_LIST = []; var USE_LOG = false; var CUR_MODELPATH = getAllUrlParams().modpath ? decodeURIComponent(getAllUrlParams().modpath) : MODELPATH; document.getElementById('TITLE').innerHTML = "<h3>Extract Bom List </h3><hr><h4>"+CUR_MODELPATH+"</h4><hr>"; window.onload = function() { //return; ThingView.init("js/ptc/thingview", function() { // ThingView should only be initialised once per frame BOM_LIST = new Array(); console.log("Creo View WebGL Viewer is now initialised"); global_number_generated = false; GLOBAL_COUNT = 0; session = ThingView.CreateSession("CreoViewWebGLDiv"); //---------------------------------- model = session.MakeModel(); ////================= Selecton Call back function definition model.SetSelectionCallback(function(type, si, idPath, selected, selType) { var JSON_OBJ = new Object; var shapeInst = session.GetShapeInstanceFromIdPath(idPath) var color = shapeInst.GetColor() var instId = shapeInst.GetIdPath(); //the shape instance instId now contains a prefix "SI_" which is bug //or at least not wanted - here remove it var instId_corrected = instId.replace(/SI_/g, ''); var instIdPath = shapeInst.GetInstanceIdPath(); console.log("idPath=" + idPath) //contains the id path e.g /1/23/3 var description = model.GetPropertyValue(idPath, "PROE Parameters", "DESCRIPTION") if (!(description == null)) console.log("description=" + description); else desciption = "---"; var sBOMPath = instId; var sBOMPath = instId_corrected; //replaced with the corrected string var sBOMIDPath = instIdPath; var sBOMID = sBOMIDPath.substring(sBOMIDPath.lastIndexOf("/") + 1) var sBOMName = sBOMPath.substring(sBOMPath.lastIndexOf("/") + 1) var sBOMDepth = instIdPath.split("/").length - 1; { console.log("NAME=part&VALUE=" + sBOMName); console.log("NAME=sBOMDepth&VALUE=" + sBOMDepth); console.log("NAME=sBOMID &VALUE=" + sBOMID); console.log("NAME=sBOMIDPath&VALUE=" + sBOMIDPath); console.log("NAME=PARTPATH&VALUE=" + sBOMName); console.log("NAME=DESCRIPTION&VALUE=" + description); console.warn("color (a=" + color.a + " b=" + color.b + " g=" + color.g + " r=" + color.r + ")"); } }); ////==================LoadFromURL with Callback model.LoadFromURLWithCallback(CUR_MODELPATH, true, true, false, function(success, isStructure, errorStack) { console.log("Model(2) LoadFromURLWithCallback - success: " + success + ", isStructure: " + isStructure); if (success) { ////============= session.SelectAllInstances() var num = session.GetSelectionCount() console.log("Number of selections =" + num) } }); ///model load from URL funciton end ///////////// }); // ThingView.init( ) end }; //window onload function end //// URL parameter handling found in the WWW Overflow and works quite good function getAllUrlParams(url) { // get query string from url (optional) or window var queryString = url ? url.split('?')[1] : window.location.search.slice(1); // we'll store the parameters here var obj = {}; // if query string exists if (queryString) { // stuff after # is not part of query string, so get rid of it queryString = queryString.split('#')[0]; // split our query string into its component parts var arr = queryString.split('&'); for (var i = 0; i < arr.length; i++) { // separate the keys and the values var a = arr[i].split('='); // set parameter name and value (use 'true' if empty) var paramName = a[0]; var paramValue = typeof(a[1]) === 'undefined' ? true : a[1]; // (optional) keep case consistent paramName = paramName.toLowerCase(); if (typeof paramValue === 'string') paramValue = paramValue.toLowerCase(); // if the paramName ends with square brackets, e.g. colors[] or colors[2] if (paramName.match(/\[(\d+)?\]$/)) { // create key if it doesn't exist var key = paramName.replace(/\[(\d+)?\]/, ''); if (!obj[key]) obj[key] = []; // if it's an indexed array e.g. colors[2] if (paramName.match(/\[\d+\]$/)) { // get the index value and add the entry at the appropriate position var index = /\[(\d+)\]/.exec(paramName)[1]; obj[key][index] = paramValue; } else { // otherwise add the value to the end of the array obj[key].push(paramValue); } } else { // we're dealing with a string if (!obj[paramName]) { // if it doesn't exist, create property obj[paramName] = paramValue; } else if (obj[paramName] && typeof obj[paramName] === 'string') { // if property does exist and it's a string, convert it to an array obj[paramName] = [obj[paramName]]; obj[paramName].push(paramValue); } else { // otherwise add the property obj[paramName].push(paramValue); } } } } return obj; } </script> </html>   When we start the  ExtractBomPVZ.html and open the chrome debugging console (Ctrl-Shift-I)     We can call the Creo View WebGl Toolkit program above in chrome with a parameter which will specify a path of the .pvz model –> example:   http://localhost:8080/ExtractBomPVZ.html?modpath=sample-data/Brake/worldcar-brake-multi-figure.pvz   The program will selects all visible components and will print to the console the idPah, partname , color etc… One problem we have here is that we could not print it to a local file because of the security restriction of the browser. A possible solution is to send the data back to the server e.g. as Json object and save the data to the server root directory. Later we can download these file if required e.g. calling the json:   http://localhost:8080/worldcar-brake-multi-figure.pvz-bomlist.json   To implement the creation of the json file we need  to change the CreoWeb.Toolkit html file. So our program  should send data to the server.  Additionaly we  also need to modify/extend the server - http.createServer() callback function to handle also the received data.     Also as next step we will extend  the Creo View Toolkit  program file by adding a XMLHttpRequest() . This request  will send the modelname and the generated json object to the http server     The call of the Toolkit html file with a start parameter was here directly in chrome via the URL but we can also call  it from a javascript or other html file:   <!DOCTYPE html> <html style="height: 100%"> <head> <meta charset="utf-8" /> <meta name="author" content="PTC"> <title>Creo View WebGL Viewer</title> </head> <script> function callBomPVZ(path) { //window.location.href = "http://localhost:8080/ExtractBomPVZ.html?modpath="+path; var myWindow = window.open("http://localhost:8080/ExtractBomPVZ.html?modpath="+path, "_blank", "height=600,width=500",false); //setTimeout(function(){ browser.refresh(); }, 1500); } </script> <body> <hr><p> <button type="button" onclick="callBomPVZ('sample-data/thingview_test/Machine-complete-CV.PVZ')">callBomPVZ:: Machine-complete-CV.PVZ</button> <hr><p><hr> <button type="button" onclick="callBomPVZ('sample-data/Brake/worldcar-brake-multi-figure.pvz')">callBomPVZ:: worldcar-brake-multi-figure.pvz</button> </body>  
View full tip
In this article  I want to consider the question how to   define a  TWX service, which could create   a  Bom and Viewable Lists from  json files. This will be very helpful option when we want to display some CreoView data in  Thingview.   How to extract BOM and viewable data form a Creo View /Illustrate *.pvz file is shown in the post "How to extract the components with properties from a pvz file".   In this example the json files are saved already  in a thingworx repository (means a thing form template FileRepostiroy) e.g.  "CAD-Files-Repository" :   1.)Create a service for the BOM List /InfoTable                 edit the CAD-Files-Repository thing and create a new service named  "GetBomStruct_arg_path" set  the BaseType : INFOTABLE . Set the DataShape property of the service - > here to BomListStuct -> this datashape need to be created  first . It should have the following Fields / property (all String type):                               create a service input parameter "the_json_path". Using this parameter we can past the repository path to the service edit the java script . We can past the following script (see the comments in the script area) var params = { path: the_json_path /* STRING */ // path it set to the input argument // example for such setting value // --> "/json_lists/Machine-complete-CV.PVZ-bomlist.json" var Content = Things["CAD-Files-Repository"].LoadJSON(params); //This will call the method LoadJSON(params) of the thing CAD-File-Reposistory var params1 = { infoTableName: undefined /* STRING */, dataShapeName: "BomListStruct" /* DATASHAPENAME */ }; // result: INFOTABLE var jsonTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params1); var result = DataShapes.ThingviewBomData.CreateValues(); // working fine for(i in Content.array){ jsonTable.AddRow({Part:Content.array[i].part, Description:Content.array[i].description, sBOMDepth:Content.array[i].sBOMDepth, sBOMID:Content.array[i].sBOMID, sBOMIDPath:Content.array[i].sBOMIDPath, sBOMName:Content.array[i].sBOMName, sBOMPath:Content.array[i].sBOMPath, path:Content.array[i].sBOMIDPath, parentPath:Content.array[i].sBOMIDPath.substring(0,Content.array[i].sBOMIDPath.lastIndexOf('/')) }); } result = jsonTable;   The parentPath  infoTable field is required for Tree Widget  to display the BOM list as tree The following code:   for(i in Content.array) { jsonTable.AddRow(Content.array[i]);}   the  code above will transfer  1 to 1  all  fields defined  in the json file with data into  infoTable fields. In this case  is not used because we have different mapping for "parentPath" -> it is new in the output InfoTable and is not contained by the json file. Test the  service:     2.)Create a service for the BOM List /InfoTable (steps are similar to 1.) edit the CAD-Files-Repository thing and create a new service named  "getViewableList_arg_path" set  the BaseType : INFOTABLE . Set the DataShape property of the service - > here to viewablelist_new -> this datashape need to be created  first . It should have the following Fields / property (all String type)     Create a service input parameter "the_json_path". Using this parameter we can past the repository path to the service Edit the java script . We can past the following script (see the comments in the script area):   var params = { //path: "/json_lists/Machine-complete-CV.PVZ-viewablelist.json" /* STRING */ path: the_json_path /* STRING */ }; var Content = Things["CAD-Files-Repository"].LoadJSON(params); var params1 = { infoTableName: undefined /* STRING */, dataShapeName: "viewablelist_new" /* DATASHAPENAME */ }; // result: INFOTABLE var jsonTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params1); var result = DataShapes.ThingviewBomData.CreateValues(); for(i in Content.array){ jsonTable.AddRow({name:Content.array[i].name, //type:Content.array[i].description, type:"viewable", value:Content.array[i].value, hasAnimation:Content.array[i].hasAnimation, hasSequence:Content.array[i].hasSequence }); } result = jsonTable;   Test the  service:      
View full tip
This post should provide more detailed steps additionally  to the posts ("How to extract the components with properties from a pvz file"[1.] and "How to use ThingView Widget from Navigate to display CAD Model/Viewables in custom mashup- Concept"[2])  This post should consider more detailed the steps for the extracting of viewables and also how to extract the sequence steps information from a .pvz / Creo Illustrate model for further usage in a Thingworx service. Following steps: 1.) Extracting the data from the Creo View Model ( Created from Creo Illustrate via publish to pvz functionality) As described in [1.] we required for the extraction of information a Creo View Toolkit.  A good choice will be the usage the Creo View  WebGL toolkit module.   A web toolkit program is called inside a html document ,where the javaScript Creo View WebGl Api is embedded. So, the most important logic could be called on the windows load function. The code below will initialize  the thingview library and  will open the pvz model file (value of variable CUR_MODELPATH in the code below  is set the complete model path):   ... window.onload = function() { ThingView.init("js/ptc/thingview", function() { // ThingView should only be initialised once per frame //---------------------------------- //send the modelname to server var xhr = new XMLHttpRequest(); xhr.open("POST", "RAY_LOG_FILE:", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); } }; //set the json modelname object var JSON_BOM_MODEL_OBJ = new Object; MODELNAME = CUR_MODELPATH.substring(CUR_MODELPATH.lastIndexOf("/") + 1) JSON_BOM_MODEL_OBJ.name = "BOMMODELNAME"; JSON_BOM_MODEL_OBJ.value = MODELNAME; xhr.send(JSON.stringify(JSON_BOM_MODEL_OBJ)) console.warn("sent BOMMODELNAME=" + MODELNAME) //finish the sending of the model_name //---------------------------------- console.log("Creo View WebGL Viewer is now initialised"); session = ThingView.CreateSession("CreoViewWebGLDiv"); //refers to the CreoViewWebGLDiv -> a div area in the html fileSource // which contains the code var xhttp = new XMLHttpRequest(); MODELNAME = CUR_MODELPATH.substring(CUR_MODELPATH.lastIndexOf("/") + 1) xhttp.open("POST", "RAY_JSON_VIEWABLE:", true); xhttp.setRequestHeader("Content-Type", "application/json"); xhttp.onreadystatechange = function() { if (xhttp.readyState === 4 && xhttp.status === 200) { var json = JSON.parse(xhttp.responseText); } }; var js_obj = new Object; js_obj.name = "VIEWABLE"; js_obj.value = MODELNAME; var data = JSON.stringify(js_obj); xhttp.send(data); model = session.MakeModel(); ////==================LoadFromURL Callback model.LoadFromURLWithCallback(CUR_MODELPATH, true, true, false, function(success, isStructure, errorStack) { var illustrations = model.GetIllustrations(); for (var i = 0; i < illustrations.size(); i++) { console.log("Illistration name: " + illustrations.get(i).name); // seems illustrations.get(i).name == pviFile model.LoadIllustrationWithCallback(illustrations.get(i).name, function(success, pviFile, stepInfoVec) { if (success === true) { var hasAnimation = model.HasAnimation() var hasSequence = model.HasSequence() xhttp.open("POST", "RAY_JSON_VIEWABLE:", true); xhttp.setRequestHeader("Content-Type", "application/json"); xhttp.onreadystatechange = function() { if (xhttp.readyState === 4 && xhttp.status === 200) { var json = JSON.parse(xhttp.responseText); } }; var js_obj = new Object; js_obj.name = pviFile; js_obj.value = pviFile; js_obj.type = "viewable"; js_obj.hasSequence = hasSequence; js_obj.hasAnimation = hasAnimation; var data = JSON.stringify(js_obj); xhttp.send(data); for (var ii = 0; ii < stepInfoVec.size(); ++ii) { xhttp.open("POST", "RAY_JSON_VIEWABLE_STEP:", true); xhttp.setRequestHeader("Content-Type", "application/json"); xhttp.onreadystatechange = function() { if (xhttp.readyState === 4 && xhttp.status === 200) { var json = JSON.parse(xhttp.responseText); } }; var step_obj = new Object; console.log("step nr=" + ii); step_obj.viewablename = pviFile; step_obj.nr = ii; step_obj.name = stepInfoVec.get(ii).name; step_obj.description = stepInfoVec.get(ii).description; var data = JSON.stringify(step_obj); xhttp.send(data); //============================================= } } }) } ////============= setTimeout(function() { { xhttp.open("POST", "RAY_JSON_VIEWABLE:", true); xhttp.setRequestHeader("Content-Type", "application/json"); xhttp.onreadystatechange = function() { if (xhttp.readyState === 4 && xhttp.status === 200) { var json = JSON.parse(xhttp.responseText); } }; var js_obj = new Object; js_obj.name = "FINISHVIEWABLES"; var data = JSON.stringify(js_obj); xhttp.send(data); } }, 10000); }); ///model load from URL funciton ///////////// }); // ThingView.init( ) }; //window onload function   The program will generate 2 different json files and will send them to the http server. When the Creo View WegGl program is started (load  the html file from the http server)  - on the server side - in the node.js console  we can see the printing of the received data - example on the picture below:     The Creo View WebGl program will create on the server side  2 json files (this requires also handling of the received  data on the server side as allready mention in the post [1.]  ) For the data extraction I used in this example the PTC demo file (provided with the installation of Creo View Toolkit) : worldcar-brake-multi-figure.pvz,  worldcar-brake-multi-figure.pvz-viewableSteplist.json   [{"viewablename":"Sequence","nr":0,"name":"Sequence","description":""}, {"viewablename":"Sequence","nr":1,"name":"Step 1","description":"Remove spring clips"}, {"viewablename":"Sequence","nr":2,"name":"Step 2","description":"Release 4 bolts"}, {"viewablename":"Sequence","nr":3,"name":"Step 3","description":"Pull apart the calipers"}] worldcar-brake-multi-figure.pvz-viewablelist.json [{"name":"Figure 1","value":"Figure 1","type":"viewable","hasSequence":false,"hasAnimation":false}, {"name":"Sequence","value":"Sequence","type":"viewable","hasSequence":true,"hasAnimation":false}, {"name":"Parts List","value":"Parts List","type":"viewable","hasSequence":false,"hasAnimation":false}, {"name":"Sectioning","value":"Sectioning","type":"viewable","hasSequence":false,"hasAnimation":false}, {"name":"Translation","value":"Translation","type":"viewable","hasSequence":false,"hasAnimation":false}, {"name":"Animation","value":"Animation","type":"viewable","hasSequence":false,"hasAnimation":true}]   2.) Definition of a service in Thingworx for the returning of the Sequence Step List (see also "Service for creating of Bom - and Viewable Lists from json files"  [3]  )    Here the first Step is to define a general service which will will convert the JSON file from a file repository to an InfoTable using particular dataShape.  The json file will be taken from a  repository:   var params = { path: the_json_path /* STRING */ }; var Content = Things[the_Repository_Name].LoadJSON(params); var params1 = { infoTableName: undefined /* STRING */, dataShapeName: the_dataShape_name /* DATASHAPENAME */ }; // result: INFOTABLE var jsonTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params1); var result = DataShapes.ThingviewBomData.CreateValues(); for(i in Content.array){ jsonTable.AddRow(Content.array[i]); } result = jsonTable; //returns the InfoTable     The service has 3 Input parameters : the_json_path, the_dataShape_name and the_Repository_Name (all are String type)       This service could be called for any filerepository, containing any json files(it is not file specific) . Via the dataShape name we will specify the filed definitions.  We need to do this  for each specific json file. In this case we have to  create first manually a DataShape which is compatible to the Json Object.  For example when we start the service RayJsonToInfoTable:     And the dataShape what we need to define for  this particular example:       For the achieving  of the final goal -> the creation of the  Sequence Step List. We need to create a  another service where we can specify the arguments for DataShape and  repository name. Example:     So the input argumets are:  I.) the path to the json file and II.) the name of the sequence for which we want to see the steps.     I am not sure if we can omit the step , where we create a  dataShape for specific json - so some kind of  dynamic dataShape generation - because in such case  we need only to specify the json file without manual editing opreration.      
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