Object displays
Every object inheriting from DessiaObject or PhysicalObject can be represented on the Dessia platform using a set of displays. For example, a display can be text, a graph, or even a 3D model. The generic objects in dessia_common offer a set of methods by default to define these different representations. More specifically, the available displays are:
- Free text using the markdown formatting options
- 2D graph
- 2D diagram
- 3D geometry
- Workflow
DessiaObject and PhysicalObject offer the ability to add representations (3D, 2D, graphs...) to each object on the platform, using decorators from the dessia_common library that are generic and can be configured by the user. Also, thanks to these decorators, each object can have multiple representations of the same type.
Display 3D
Customizing the 3D display requires having a method decorated with the @cad_view
decorator that returns the babylon_data
of the VolumeModel
object from the volmdlr
module.
For example, to define the 3D presentation of theBall
object, we have separated the process into two methods:volmdlr_primitives
, where we use themake_sphere
method of theSolid
object from theshapes
module ofvolmdlr
to create an elementary primitive constituting a sphere, and thecad_view
method which retrieves the primitives from the first method (in this case, we only have one), puts them in a VolumeModel, and returns the babylon_data of the latter. When this object is opened from the Dessia platform, a 3D viewer will be integrated into the object's display to represent its geometry, having as selector what is defined in thecad_view
decorator.
Ball objects:
import math
from math import pi
from dessia_common.core import PhysicalObject
from dessia_common.decorators import cad_view
from volmdlr import OXYZ, Z3D, Point2D, Point3D, Frame3D, Vector3D
from volmdlr.core import VolumeModel
from volmdlr.curves import Circle2D
from volmdlr.wires import Contour2D
from volmdlr.shapes import Solid
class Ball(PhysicalObject):
_standalone_in_db = False
def __init__(self, diameter: float, name: str = ''):
self.diameter = diameter
PhysicalObject.__init__(self, name=name)
def volmdlr_primitives(self, pos_x=0., pos_y=0., pos_z=0.,
distance=0., angle=0.):
center_x = pos_x + distance * math.cos(angle)
center_y = pos_y + distance * math.sin(angle)
center_z = pos_z
center = (center_x, center_y, center_z)
u_axis = Vector3D(1, 0, 0)
v_axis = Vector3D(0, 1, 0)
w_axis = Vector3D(0, 0, 1)
frame = Frame3D(
origin=Point3D(*center),
u=u_axis,
v=v_axis,
w=w_axis)
sphere = Solid.make_sphere(
radius=self.diameter / 2,
frame=frame,
angle1=-math.pi / 2,
angle2=math.pi / 2,
angle3=2 * math.pi,
name='sphere')
return [sphere]
@cad_view(selector='Ball CAD')
def cad_view(self):
primitives = self.volmdlr_primitives()
return VolumeModel(primitives=primitives).babylon_data()
class Bearing(PhysicalObject):
_standalone_in_db = True
def __init__(self, ball: Ball, internal_diameter: float,
external_diameter: float, height: float,
thickness: float = 0., name: str = ''):
self.ball = ball
self.internal_diameter = internal_diameter
self.external_diameter = external_diameter
self.height = height
self.thickness = thickness
PhysicalObject.__init__(self, name=name)
def volmdlr_primitives(self, pos_x=0., pos_y=0., pos_z=0.,
number_balls=10):
# Setup circles for extrusions
outer_circle = Circle2D.from_center_and_radius(
center=Point2D(pos_x, pos_y),
radius=self.external_diameter / 2)
inner_outer_circle = Circle2D.from_center_and_radius(
center=Point2D(pos_x, pos_y),
radius=self.external_diameter / 2 - self.thickness)
inner_circle = Circle2D.from_center_and_radius(
center=Point2D(pos_x, pos_y),
radius=self.internal_diameter / 2)
outer_inner_circle = Circle2D.from_center_and_radius(
center=Point2D(pos_x, pos_y),
radius=self.internal_diameter / 2 + self.thickness)
# Extrusions
frame = OXYZ.copy()
frame.origin += Z3D * (pos_z - self.height/2)
outer_extrusion = Solid.make_extrusion_from_frame_and_wires(
frame=frame, extrusion_length=self.height, outer_contour2d=Contour2D.from_circle(outer_circle),
inner_contours2d=[Contour2D.from_circle(inner_outer_circle)])
inner_extrusion = Solid.make_extrusion_from_frame_and_wires(
frame=frame, extrusion_length=self.height, outer_contour2d=Contour2D.from_circle(outer_inner_circle),
inner_contours2d=[Contour2D.from_circle(inner_circle)])
# Balls
ball_primitives = []
ball_distance = self.internal_diameter / 2 + \
(self.external_diameter - self.internal_diameter) / 4
for i in range(number_balls):
ball_primitive = self.ball.volmdlr_primitives(
pos_x=pos_x, pos_y=pos_y, pos_z=pos_z,
distance=ball_distance,
angle=i * 2 * pi / number_balls)[0]
ball_primitives.append(ball_primitive)
return [outer_extrusion, inner_extrusion] + ball_primitives
@cad_view(selector='Bearing CAD')
def cad_view(self):
primitives = self.volmdlr_primitives()
return VolumeModel(primitives=primitives).babylon_data()
Display 2D
Five types of 2D display are possible: a Scatter, an Histogram, a Draw, a Graph and a Parallel Plot. All five display modes require the use of the "plot_data" library developed by Dessia. This is a Python library that is structured in a similar way to "volmdlr" and whose components are decomposed from Python objects inheriting from DessiaObject. Similarly, all objects can be stored from the Dessia platform. For more details on the "plot_data" library, please refer to the 2D graphics chapter.
2D display from the "plot_data" library requires custom objects to implement the "plot_data_view" decorator. When a method is assigned this decorator, the Dessia platform will offer a graphical representation in the object's display area. It is important to realize that the term "plot_data" is used to define 2 different concepts:
- Name of the 2D representation library
- Name of the file format allowing to write all components of the plot_data library in a generic way
For ease of use, the volmdlr library provides methods for generating these plot_data objects for its 2D objects (which are required for 2D plotting). For more details, it is necessary to refer to the plot_data documentation (opens in a new tab) (opens in a new tab).
The following code represents this method for generating a 2D representation of Ball and Bearing objects:
from math import cos, pi, sin
from dessia_common.core import PhysicalObject
from dessia_common.decorators import plot_data_view, cad_view
from plot_data import EdgeStyle, PrimitiveGroup, SurfaceStyle
from plot_data.colors import BLACK, GREY
from volmdlr import Point2D
from volmdlr.curves import Circle2D
class Ball(PhysicalObject):
_standalone_in_db = False
def __init__(self, diameter: float, name: str = ''):
self.diameter = diameter
PhysicalObject.__init__(self, name=name)
@plot_data_view("2D display for Ball")
def display_2d(self, pos_x=0., pos_y=0., distance=0., angle=0.):
# Color settings
edge_style = EdgeStyle(color_stroke=BLACK)
surface_style = SurfaceStyle(color_fill=GREY, opacity=0.5)
ball_x = distance * cos(angle)
ball_y = distance * sin(angle)
center = Point2D(pos_x + ball_x, pos_y + ball_y)
circle = Circle2D.from_center_and_radius(
center=center, radius=self.diameter / 2)
primitives = [circle.plot_data(
edge_style=edge_style, surface_style=surface_style)]
return PrimitiveGroup(primitives=primitives,
name='Circle')
class Bearing(PhysicalObject):
_standalone_in_db = True
def __init__(self, ball: Ball, internal_diameter: float,
external_diameter: float, height: float,
thickness: float = 0., name: str = ''):
self.ball = ball
self.internal_diameter = internal_diameter
self.external_diameter = external_diameter
self.height = height
self.thickness = thickness
PhysicalObject.__init__(self, name=name)
@plot_data_view("2D display for Bearing")
def display_2d(self, pos_x=0., pos_y=0., number_balls=1):
# Color settings
edge_style = EdgeStyle(color_stroke=BLACK)
surface_style = SurfaceStyle(opacity=0)
primitives = []
center = Point2D(pos_x, pos_y)
# External circle
external_circle = Circle2D.from_center_and_radius(
center=center, radius=self.external_diameter / 2)
primitives.append(external_circle.plot_data(
edge_style=edge_style, surface_style=surface_style))
# Internal circle
internal_circle = Circle2D.from_center_and_radius(
center=center, radius=self.internal_diameter / 2)
primitives.append(internal_circle.plot_data(
edge_style=edge_style, surface_style=surface_style))
# Balls
ball_distance = self.internal_diameter / 2 + \
(self.external_diameter - self.internal_diameter) / 4
for i in range(number_balls):
primitives.extend(self.ball.display_2d(
pos_x=pos_x,
pos_y=pos_y,
distance=ball_distance,
angle=i * 2 * pi / number_balls).primitives)
return PrimitiveGroup(primitives=primitives)
For the Ball
object, we start by using the volmdlr library to define the 2D geometric shapes.
Then, from the "plot_data()" method of volmdlr objects, we generate
these generic plot_data objects that can be used to fill PrimitiveGroup.
If you wish to include a scatter plot in the plot_data method, here is an example of a possible data input:
from dessia_common.core import PhysicalObject
import random
from plot_data import colors
import plot_data.core as plot_data
from dessia_common.decorators import plot_data_view
class Graph(PhysicalObject):
def __init__(self, diameter: float,
name: str = ""):
self.diameter = diameter
PhysicalObject.__init__(self, name=name)
@plot_data_view("ScatterPlot")
def my_scatter_plot(self):
elements = []
SHAPES = ['round', 'square', 'triangle', 'ellipse']
COLORS = [colors.RED, colors.BLUE, colors.GREEN, colors.YELLOW,
colors.ORANGE, colors.VIOLET]
for i in range(50):
random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)]
random_color = COLORS[random.randint(0, len(SHAPES) - 1)]
elements.append(plot_data.Sample({'mass': random.uniform(0, 50),
'length': random.uniform(0, 100),
'shape': random_shape,
'color': random_color}))
return plot_data.Scatter(elements=elements, x_variable='mass',
y_variable='length')
A large number of different graphs are possible and detailed in the plot_data documentation (opens in a new tab) (opens in a new tab).
Display markdown
To include free text in the display, it is possible to write a method in custom objects that returns a string formatted according to the markdown standard (# for a Heading 1, ## for a Heading 2, etc.). It is necessary to insert the '\n' character into the string when a line break is desired.
Note that for the markdown display, it is the only display for which you do not need to use a decorator for it to work by naming the method to_markdown
. However, to have multiple markdown displays on the same object, you must use the markdown_view
decorator for it to be integrated into your object's displays, and in this case, you can give the function any name you want.
The following code presents an example of how to apply this to the Bearing object:
class Bearing(PhysicalObject):
_standalone_in_db = True
def __init__(self, ball: Ball,
internal_diameter: float,
external_diameter: float,
height: float,
name: str = ""):
self.ball = ball
self.internal_diameter = internal_diameter
self.external_diameter = external_diameter
self.height = height
PhysicalObject.__init__(self, name=name)
def to_markdown(self):
infos = ''
infos += '## Bearing Infos \n\n'
infos += '|name|height|external_diameter|internal_diameter|' + '\n'
infos += '|:---------------:|:---------------:|:---------------:|:-----------------:|' + '\n'
infos += '|' + self.name + '|' + str(self.height) + '|' + str(self.external_diameter) \
+ '|' + str(self.internal_diameter) + '|\n'
return infos