-
Notifications
You must be signed in to change notification settings - Fork 1
VizRep

The VizRep is a specialized domain specific language (DSL) designed for the specific purpose of defining the visual representation of all model concepts which are represented visually within a model. The VizRep has a predefined set of methods which can be used for the creation of your own visual representations.
In the modeling tool a clientside TypeScript class handles the translation of these generic methods to the visual representation with the technology of the web client. For the modeling tool shown in this project this is the JavaScript library three.js. However, one could also create other visual translators of the VizRep to other technology stacks.
The VizRep is always built in the same way. The set of available language constructs is described below.
The VizRep is basically a normal JavaScript function that is passed to the generater class during runtime in the client. The structure is always the same. Be aware that the VizRep is an async function. If you are not familiar with async function have a look this page:
async function vizRep(gc) {
your visual representation code ...
}In the following the different language concepts are explained.
The VizRep takes an argument called gc (GraphicContext). This is a class in the client side code which provides all the methods necessary for the visual representation. In the following we look at methods from the Graphic Context.
The ability to retrieve attribute values dynamically is facilitated by using Expressions (See Expressions). For example, you can pass the universally unique identifier (UUID) or the name of the desired attribute to the "gc" object. This enables the retrieval of the attribute instance, as assigned to the specified class instance, from the given meta-attribute. The function, which returns a string value, can be represented as follows:
attrvalByName(attrName: string)
attrval(attrUUID: string)async function vizRep(gc) {
// This is the line where the expression is called
await gc.expression.attrval('d6632c72-89fa-4210-9d01-18e911505608');
}async function vizRep(gc) {
// This is the line where the expression is called
await gc.expression.attrvalByName('Name');
}The function get_current_class_instance_uuid() retrieves the universally unique identifier (UUID) of the currently active class instance. This function is called on the instanceUtility object (gc.get_current_class_instance_uuid() is not supportet anymore), and it returns the UUID of the class instance that is currently being actively used within the system.
gc.instanceUtility.get_current_class_instance_uuid()The function gc.get_current_port_instance_uuid() retrieves the universally unique identifier (UUID) of the currently active port instance. This function is called on the instanceUtility object (gc.get_current_port_instance_uuid() is not supportet anymore), and it returns the UUID of the port instance that is currently being actively used within the system.
gc.instanceUtility.get_current_port_instance_uuid()You can define variables inside the function like in a normal JavaScript function. Thereby you can store
values like for example:
async function vizRep(gc) {
let example = 'hello world!';
}You can get dynamic values of runtime instances at the creation of a visual representation. For example, if you create a BPMN task instance, which has an attribute instance of the attribute 'name' (uuid of the attribute name = 'd6632c72-89fa-4210-9d01-18e911505608'), you can get the instance value of this attribute instance dynamically by the following concept:
async function vizRep(gc) {
let name = await gc.expression.attrvalByName('Name')
}This will save the instance value of the attribute instance name of the current class instance in the variable name.
To define variables in a dynamic approach, you can further define them with the set and get concept. Thereby, you can later on access this variables dynamically during runtime.
async function vizRep(gc) {
await gc.setVariable(
'name',
await gc.expression.attrvalByName('Name'),
false);
}The arguments are:
- The string "name" -> the name of the variable you want to set.
- The result of an asynchronous call to the "expression.attrvalByName" method on the "gc" object (generater class on the client side), passing in a string "name" to get the value from (this must be the name of the meta attribute and not the attribute instance).
- The boolean value "false" -> this indicates whether the value shall be dynamically changeable or if it is a static set.
For requesting the value with the getter methode you can call:
async function vizRep(gc) {
await gc.getVariableValue('name')
}This method is used to create a cube graphic with specific width, height, and depth.
async graphic_cube(width: number, height: number, depth: number, color?: string, map?: string, x_rel?: number, y_rel?: number, z_rel?: number) {}Parameters
-
width: a number that represents the width of the cube. -
height: a number that represents the height of the cube. -
depth: a number that represents the depth of the cube. -
color: an optional string parameter that represents the color of the cube. -
map: an optional string parameter that represents the texture/map applied to the cube. -
x_rel, y_rel, z_rel: an optional nummer to set the offset of a cube in relation to the center of the visualization.
example:
async function vizRep(gc) {
let map = 'This is a not working string. Put your base64 string here';
await gc.graphic_cube(0.1, 0.1, 0.02, 'grey', map);
}or when also setting a position (here without a map):
async function vizRep(gc) {
await gc.graphic_cube(0.1, 0.1, 0.02, 'grey', undefined, 1, 0, 0 );
}This method is used to create a plane graphic with specific width and height.
async graphic_plane(width: number, height: number, color?: string, map?: string, x_rel?: number, y_rel?: number, z_rel?: number ) {}Parameters
-
width: a number that represents the width of the plane. -
height: a number that represents the height of the plane. -
color: an optional string parameter that represents the color of the plane. -
map: an optional string parameter that represents the texture/map applied to the plane. -
x_rel, y_rel, z_rel: an optional nummer to set the offset of a plane in relation to the center of the visualization.
example:
async function vizRep(gc) {
let map = 'This is a not working string. Put your base64 string here';
let icon = map;
await gc.graphic_plane(1, 1, 'grey', map)
}or when positioning the plane a x=1 inside a visualization (no map this time):
async function vizRep(gc) {
let map = 'This is a not working string. Put your base64 string here';
let icon = map;
await gc.graphic_plane(1, 1, 'grey', undefined, 1, 0, 0)
}!!! Attention! If two planes are positioned identically, a phenomenon known as z-fighting may occur, resulting in the appearance of flickering materials. It is essential to ensure that this does not occur in a graphic object. Alternatively, the use of extremely thin cubes in place of multiple planes allows for variation in depth.
This method is used to create a sphere graphic with specific radius and segments.
async graphic_sphere(radius: number, withSegments: number, heightSegments: number, color?: string, map?: string, x_rel?: number, y_rel?: number, z_rel?: number) {}Parameters
-
radius: a number that represents the radius of the sphere. -
withSegments: a number that represents the number of horizontal segments of the sphere. -
heightSegments: a number that represents the number of vertical segments of the sphere. -
color: an optional string parameter that represents the color of the sphere. -
map: an optional string parameter that represents the texture/map applied to the sphere. -
x_rel, y_rel, z_rel: an optional nummer to set the offset of a sphere in relation to the center of the visualization.
example:
await gc.graphic_sphere(0.05, 10, 10, 'black');or when using relative positioning (z=1) and no map as texture:
await gc.graphic_sphere(0.05, 10, 10, 'black', undefined, 0, 0, 1);There is always the possibility to combine the object cube, plane and sphere with a texture map. This texture map must be procured as base64 string. If the map-argument is not available, the object will have the color that is given in the color-attribute. If you define multiple graphical objects in one VizRep, they will be combined as one object. By default, the position of each independent object is (0,0,0). If you use the optional x_rel, y_rel, and z_rel parameters, you can choose to place objects relative to the origin (0,0,0) of the visualization.
This method is used to create a predefined 3D object by using a gltf file. If you are not familiar with the gltf file format, have a look at the Link.
For using a gltf file, create your own gltf, e.g., with the THREE.JS-Editor. Make sure that all objects you want to import in the VizRep are on the first level of the scene, e.g., are direct children of the scene. All meshes in the scene should be assigned the material "meshPhongMaterial". If this is not the case, all materials are converted during import. This can lead to changes in appearence, e.g., in color. The scene should then be exported as a .gltf object and the file should be opened in an editor and have all line breaks removed, resulting in a single line string. (You can use the online tool LingoJam) This string can then be passed as an argument to the gc function "gc.graphic_gltf(string)" in the VizRep, which will add the objects to the graphic context and merge them with any other objects defined in the VizRep. It is important to note that adding images and textures directly to the .gltf file will increase the file size and slow down the gltf loader. Additionally, ensure that all geometries in the scene are of the type 'MeshBufferGeometry' to avoid positioning issues.
async graphic_gltf(objectString: string, x_rel?: number, y_rel?: number, z_rel?: number) {}Parameters
-
objectString: a string of a GLTF file, without line breaks. This is basically a JSON. -
x_rel, y_rel, z_rel: an optional nummer to set the offset of a 3D object in relation to the center of the visualization.
The graphic_gltf method is an asynchronous method that takes an argument, objectString, which is a string representation of a GLTF object, as well as optional positioning parameters. Inside the method, a new GLTFLoader is defined, which is used to parse the objectString and convert it into a set of three.js objects. The parsed objects are then added to the gc.object3D and the findType method is called to find the objects of type 'Mesh' and add them to the object3D. The method doesn't return any value.
example:
async function vizRep(gc) {
let gltf = 'your gltf sting without line breaks';
// Draw the GLTF object using the specified gltf variable
await gc.graphic_gltf(gltf);
}or when positioning the GLTF 2 meters on the x-axis relative to the center of the visualization:
async function vizRep(gc) {
let gltf = 'your gltf sting without line breaks';
// Draw the GLTF object using the specified gltf variable
await gc.graphic_gltf(gltf, 2, 0, 0);
}This method is used to create a text label with specific relative x, y and z position in relation to the objects origin (0,0,0), size and height.
async graphic_text(x_rel: number, y_rel: number, z_rel: number, size: number, color: string, att: string, pos_name_x?: string, pos_name_y?: string, pos_name_z?: string, rx?: number, ry?: number, rz?: number, rw?: number) {}Parameters
-
x_rel: a number value representing the x-coordinate of the text's position relative to the object it's attached to. -
y_rel: a number value representing the y-coordinate of the text's position relative to the object it's attached to. -
z_rel: a number value representing the z-coordinate of the text's position relative to the object it's attached to. -
size: a number value representing the size of the text. -
color: a string value representing the color of the text, it could be a named color, a hex color code or a rgb color code. -
att: a string value representing the attribute that the text will display. -
pos_name_x(optional): a string value representing the name of the variable that holds the x-coordinate of the text's position -> this means you can define what variable you want to use for the positioning of your label. -
pos_name_y(optional): a string value representing the name of the variable that holds the y-coordinate of the text's position. -
pos_name_z(optional): a string value representing the name of the variable that holds the z-coordinate of the text's position. - rx, ry, rz, rw: a number value representing the rotation parameter of the text label. This are optional variables. If you do not specify it in your graphic_text, the label cannot be rotated.
Example:
await gc.graphic_text(-0.3, 0, 0.1, 0.2, 'gray', 'Attribute_name');
// other example
await gc.graphic_text(await gc.getVariableValue('x_rel'), await gc.getVariableValue('y_rel'), await gc.getVariableValue('z_rel'), 0.2, 'black', await gc.getVariableValue('name'), 'x_rel', 'y_rel', 'z_rel');This method is used to create a line for a relation with specific color, width, and whether it should be dashed or not.
Parameters
-
color: a string that represents the color of the line. -
line_width: a number that represents the width of the line. -
dashed: a boolean that represents whether the line should be dashed or not. -
dash_scale: a number that represents the scale of the dashes. -
dash_size: a number that represents the size of the dashes. -
gap_size: a number that represents the size of the gaps between dashes.
You can define the end- and start object of a relation in the VizRep using the following methods:
rel_from_object(object)
rel_to_object(object)As object input you have to pass on another 3D object. You can use nesting to pass on other objects like cubes or spehres.
await gc.rel_from_object(
await gc.graphic_cube(0.006, 0.006, 0.006, 'black')
);You can also define labels for relations, i.e., rel_graphic_text_from(object), rel_graphic_text_middle(object), and rel_graphic_text_to(object), taking graphic_text as input objects.
examples:
await gc.rel_graphic_text_from(await gc.graphic_text(-0.40, 0, 0, 0.1, 'green', 'A', ...not mandatory position parameters.));
await gc.rel_graphic_text_middle( await gc.graphic_text(0, 0, 0, 0.2, 'black', 'weight')); --> you could also use dynamic values instead of strings.
In the middle text the position is calculated during runtime. Thus, It does not matter what position you set.
example:
async function vizRep(gc) {
let map = '... your base64 string';
let icon = map;
await gc.rel_graphic_line('black', 0.002, false, 0, 0, 0);
await gc.rel_from_object(
await gc.graphic_cube(0.006, 0.006, 0.006, 'white')
);
await gc.rel_to_object(
await gc.graphic_plane(0.1, 0.1, 'grey', map)
);
await gc.setVariable('x_rel', -0.1, true);
await gc.setVariable('y_rel', 0.2, true);
await gc.setVariable('z_rel', 0.05, true);
await gc.rel_graphic_text_to(
await gc.graphic_text(await gc.getVariableValue('x_rel'), await gc.getVariableValue('y_rel'), await gc.getVariableValue('z_rel'), 0.2, 'black',
await gc.getVariableValue('weight'), 'x_rel', 'y_rel', 'z_rel')
);
}This is an example of a vizRep function that is used to define the visual representation of relations between concepts in a model. The function makes use of several methods provided by the gc object.
The function first sets the map variable to a base64 encoded image. Then it sets the icon variable to the same value. Then it creates a line for the relation with the rel_graphic_line method, it defines the color of the line as "black", width of the line as 0.002, not dashed, dash scale as 0, dash size as 0 and gap size as 0.
The function also defines the start and end objects of the relation using the rel_from_object and rel_to_object methods. The rel_from_object method creates a cube graphic with specific width, height, and depth and color as white. The rel_to_object method creates a plane graphic with specific width and height and color as grey and with a texture/map.
The function also defines the labels of the relation using the rel_graphic_text_from method, it creates a text graphic with specific relative x, y and z position, size and height and it takes the value of the attribute "A" as the content of the text.
//other example
async fuction vizRep(gc) {
let map = 'your base64 string for the end object map, e.g., an arrow';
let icon = 'your base64 string for the icon';
await gc.rel_graphic_line('black', 0.002, false, 0, 0, 0);
await gc.rel_from_object(await gc.graphic_cube(0.006, 0.006, 0.006, 'white'));
await gc.rel_to_object(await gc.graphic_plane(0.1, 0.1, 'grey', map));
}This is an example of a vizRep function that is used to define the visual representation of a port. The function makes use of the gc object to create a cube graphic with specific width, height, depth, color and texture/map. The definition of ports is exactly the same as for classes.
The graphic_cube method creates a cube graphic with specific width, height, depth, color and texture/map, the color of the cube is grey and the texture/map is passed as an argument to the method.
async function vizRep(gc) {
let map = 'data:image/png;base64,iVBORw0KG...';
await gc.graphic_cube(0.1, 0.1, 0.02, 'grey', map);
await gc.setVariable('name', await gc.expression.attrvalByName('Name'), false);
await gc.setVariable('x_rel', 0, true);
await gc.setVariable('y_rel', -0.6, true);
await gc.setVariable('z_rel', 0.3, true);
await gc.graphic_gltf(gltf);
await gc.graphic_text(
await gc.getVariableValue('x_rel'),
await gc.getVariableValue('y_rel'),
await gc.getVariableValue('z_rel'),
0.2,
'gray',
await gc.getVariableValue('name'),
'x_rel',
'y_rel',
'z_rel'
);
}Ports alone cannot be used. They are always used in combination with other concepts, e.g., classes or scene types. This is explained below.
Whether a class has ports, is not defined in the vizRep. This is defined by the metamodel of the language. Ports are added to a class by adding them to the ports[] array inside a class definition. At the moment there can only be one port_instance of each port and they are automatically attached to the class_instance during creation. The position of a port in relation to its parent class can be defined with the attribute "relative_coordinate_3d".
"relative_coordinate_3d": {"x":-10, "y":0, "z":0}You can define an icon for the menu entry in the client. If you want to use the same icon as is used as map, just add the variable ’let icon = map’. You can also pass another bas64 image as icon if you prefer:
let map = 'data:image/svg+xml;base64,PHN2...';
let icon = map;
or
let icon = 'your base64 string';This method is used to create a clickable 3D object that can trigger specific actions or expressions when clicked.
async graphic_button(object: THREE.Mesh | THREE.Mesh[], expression?: string) {}Parameters
-
object: a THREE.Mesh or an array of THREE.Mesh objects that represent the button's visual appearance. This is a normal 3D object that is created in the VizRep. The object can be a cube, plane, sphere, or any other 3D object that you want to use as a button. -
expression: an optional string parameter that represents the expression/function to be executed when the button is clicked. If not provided, the button will not have any action associated with it.
Example:
async function vizRep(gc) {
let map = 'data:image/png;base64,iVBORw0KGgoAAAAN...o/h2o9sun';
let icon = map;
await gc.graphic_button(
await gc.graphic_cube(0.5 + 0.05, 0.25 + 0.05, 0.055, 'red', undefined, 0.2, 0.25, 0),
'async function simulation(expression,instance){console.log("I run the simulation function that is stored on the button for the instance "+instance.uuid);}'
);
}In this example, a button is created using a red cube with specific dimensions. When the button is clicked, it will execute the provided expression, which logs a message to the console along with the UUID of the instance associated with the button.
A button action (simulation function) should be defined in a predefined way. The function should be an async function that takes two parameters: expression and instance. The expression parameter is the expression singleton class that is passed to the function. This means that you can use predefined MM-AR expressions by calling expression.yourexpression(...). The instance parameter is the instance of the object that the button is associated with. Inside the function, you can define any actions or logic you want, including calling other functions or methods of the expression class.
async function vizRep(gc) {
let map = 'add a base64 image string';
// set the variable 'name' to the value of the attribute with UUID 'd6632c72-89fa-4210-9d01-18e911505608' for the current class_instance
await gc.setVariable(
'name',
await gc.expression.attrvalByName('Name'),
false
);
//set the variable 'x_rel' to -0.3
await gc.setVariable(
'x_rel',
-0.3,
true
);
//set the variable 'y_rel' to 0
await gc.setVariable(
'y_rel',
0,
true
);
//set the variable 'z_rel' to 0.1
await gc.setVariable(
'z_rel',
0.1,
true
);
// create a cube graphic with specific width, height, depth, color and texture/map
await gc.graphic_cube(
5,
5,
0.00002,
'grey',
map
);
//create a text graphic with specific relative x, y, z position, size, color and font color and text
await gc.graphic_text(
await gc.getVariableValue('x_rel'),
await gc.getVariableValue('y_rel'),
await gc.getVariableValue('z_rel'),
0.2,
'gray',
await gc.getVariableValue('name'),
'x_rel',
'y_rel',
'z_rel'
);
}or an example with a gltf file:
async function vizRep(gc) {
let gltf = 'your gltf sting without line breaks';
let map = 'data:image/svg+xml;b...PC9nPjwvc3ZnPg==';
let icon = map;
await gc.setVariable(
'name',
await gc.expression.attrvalByName('Name'),
false
);
//set the variable 'x_rel' to -0.3
await gc.setVariable(
'x_rel',
-0.3,
true
);
//set the variable 'y_rel' to 0
await gc.setVariable(
'y_rel',
0,
true
);
//set the variable 'z_rel' to 0.1
await gc.setVariable(
'z_rel',
0.1,
true
);
//create an object by using a gltf string as input
await gc.graphic_gltf(gltf);
//create a text graphic with specific relative x, y, z position, size, color and font color and text
await gc.graphic_text(
await gc.getVariableValue('x_rel'),
await gc.getVariableValue('y_rel'),
await gc.getVariableValue('z_rel'),
0.2,
'red',
await gc.getVariableValue('name'),
'x_rel',
'y_rel',
'z_rel'
);
}an example with a button:
async function vizRep(gc) {
let incomingArcs = await gc.expression.attrvalByName('Incoming Arcs');
let outgoingArcs = await gc.expression.attrvalByName('Outgoing Arcs');
let enoughTokens = await gc.expression.attrvalByName('Enough Tokens');
let color1 = (incomingArcs == 'true' && outgoingArcs == 'true')
? '#ff2e2e'
: '#0088ff';
let color2 = (incomingArcs == 'true' && outgoingArcs == 'true')
? '#d10000'
: '#004499';
await gc.graphic_cube(0.5, 1, 0.05, color1);
await gc.graphic_cube(0.5 + 0.05, 1 + 0.05, 0.03, color2);
if (enoughTokens == 'true') {
await gc.graphic_cube(
0.5,
0.25,
0.06,
'#d10000',
undefined,
0.2,
0.25,
0
);
await gc.setVariable('fire_x_rel', 0.2, true);
await gc.setVariable('fire_y_rel', 0.25, true);
await gc.setVariable('fire_z_rel', 0.04, true);
await gc.graphic_text(
await gc.getVariableValue('fire_x_rel'),
await gc.getVariableValue('fire_y_rel'),
await gc.getVariableValue('fire_z_rel'),
0.2,
'black',
'FIRE',
'fire_x_rel',
'fire_y_rel',
'fire_z_rel'
);
await gc.graphic_button(
await gc.graphic_cube(
0.5 + 0.05,
0.25 + 0.05,
0.055,
'#a30000',
undefined,
0.2,
0.25,
0
),
'async function simulation(expression,instance){console.log("I run the simulation function that is stored on the button for the instance "+instance.uuid);}'
);
}
await gc.setVariable('name', await gc.expression.attrvalByName('Name'), false);
await gc.setVariable('name_x_rel', 0, true);
await gc.setVariable('name_y_rel', -0.8, true);
await gc.setVariable('name_z_rel', 0.05, true);
await gc.graphic_text(
await gc.getVariableValue('name_x_rel'),
await gc.getVariableValue('name_y_rel'),
await gc.getVariableValue('name_z_rel'),
0.2,
'black',
await gc.getVariableValue('name'),
'name_x_rel',
'name_y_rel',
'name_z_rel'
);
let icon = 'data:image/png;base64,iVBORw0KGgoAAAANS...';
}async function vizRep(gc) {
// Declare a variable to store the image map as a base64 encoded string
let map = 'data:image/png;base64,iVBORw0KGgoAAAAN...o/h2o9sun';
// Draw a black line with the specified properties
await gc.rel_graphic_line(
'black',
0.002,
false,
0,
0,
0
);
// Draw the starting object of the relation as a white cube with the specified dimensions
await gc.rel_from_object(
await gc.graphic_cube(
0.006,
0.006,
0.006,
'white'
)
);
// Draw the ending object of the relation as a grey plane with the specified dimensions and image map
await gc.rel_to_object(
await gc.graphic_plane(
0.5,
0.3,
'grey',
map
)
);
}rel_graphic_line(color: string, line_width: number, dashed: boolean, dash_scale: number, dash_size: number, gap_size: number)
- color: a string value representing the color of the line, it could be a named color, a hex color code or a rgb color code.
- line_width: a number value representing the width of the line in the 3D scene, it is typically a small value, such as 0.002.
- dashed: a boolean value indicating whether the line should be drawn as a dashed line or not.
- dash_scale: a number value that changes the length of the dashes and spaces.
- dash_size: a number value that changes the length of the dashes.
- gap_size: a number value that changes the length of the spaces between the dashes.
rel_from_object(object) & rel_to_object(object)
- object: a 3D object that represents the starting or ending point of the relation line.
graphic_cube(width: number, height: number, depth: number, color?: string, map?: string)
- width: a number value representing the width of the cube in the 3D scene.
- height: a number value representing the height of the cube in the 3D scene.
- depth: a number value representing the depth of the cube in the 3D scene.
- color: a string value representing the color of the cube, it could be a named color, a hex color code or a rgb color code.
- map: the url of an image that should be mapped on the cube.
graphic_plane(width: number, height: number, color?: string, map?: string)
- width: a number value representing the width of the plane in the 3D scene.
- height: a number value representing the height of the plane in the 3D scene.
- color: a string value representing the color of the plane, it could be a named color, a hex color code or a rgb color code.
- map: the url of an image that should be mapped on the plane.
async function vizRep(gc) {
// Declare a variable to store the image map as a base64 encoded string
let map = 'data:image/png;base64,iVBORw0KGgoAAAAN...o/h2o9sun';
// Draw a grey cube with the specified dimensions and image map
await gc.graphic_cube(0.1, 0.1, 0.02, 'grey', map);
}