@@ -70,27 +70,28 @@ def __init__(
7070 self .cam = mujoco .MjvCamera ()
7171 self .scn = mujoco .MjvScene (self .model , maxgeom = 10000 )
7272 self .pert = mujoco .MjvPerturb ()
73- self .fig = mujoco .MjvFigure ()
74- mujoco .mjv_defaultFigure (self .fig )
75-
76- # Counter for number of lines added to plot
77- self ._line_counter = 0
7873
7974 self .ctx = mujoco .MjrContext (
8075 self .model , mujoco .mjtFontScale .mjFONTSCALE_150 .value )
8176
82- # Adjust placement and size of graph
8377 width , height = glfw .get_framebuffer_size (self .window )
78+
79+ # figures for creating 2D plots
80+ max_num_figs = 3
81+ self .figs = []
82+ self .figs_viewport = []
8483 width_adjustment = width % 4
85- self .graph_viewport = mujoco .MjrRect (
86- int (3 * width / 4 ) + width_adjustment ,
87- 0 ,
88- int (width / 4 ),
89- int (height / 4 ),
90- )
91- mujoco .mjr_figure (self .graph_viewport , self .fig , self .ctx )
92- self .fig .flg_extend = 1
93- self .fig .flg_symmetric = 0
84+ fig_w , fig_h = int (width / 4 ), int (height / 4 )
85+ for idx in range (max_num_figs ):
86+ fig = mujoco .MjvFigure ()
87+ mujoco .mjv_defaultFigure (fig )
88+ fig .flg_extend = 1
89+ self .figs .append (fig )
90+
91+ x = int (3 * width / 4 ) + width_adjustment
92+ y = idx * fig_h
93+ vp = mujoco .MjrRect (x , y , fig_w , fig_h )
94+ self .figs_viewport .append (vp )
9495
9596 # load camera from configuration (if available)
9697 pathlib .Path (
@@ -137,59 +138,50 @@ def __init__(
137138 self ._overlay = {}
138139 self ._markers = []
139140
140- def add_graph_line (self , line_name ):
141- assert (
142- type (line_name ) == str
143- ), f"Line_name is not a string: { type (line_name )} "
144- if line_name in []:
141+ def add_line_to_fig (self , line_name , fig_idx = 0 ):
142+ assert isinstance (line_name , str ), \
143+ "Line name must be a string."
144+
145+ fig = self .figs [fig_idx ]
146+ if line_name .encode ('utf8' ) == b'' :
147+ raise Exception (
148+ "Line name cannot be empty."
149+ )
150+ if line_name .encode ('utf8' ) in fig .linename :
145151 raise Exception (
146- "line name already exists"
152+ "Line name already exists in this plot. "
147153 )
148154
149- self .fig .linename [self ._line_counter ] = line_name
155+ # this assumes all lines added by user have a non-empty name
156+ linecount = fig .linename .tolist ().index (b'' )
157+
158+ # we want to add the line after the last non-empty index
159+ fig .linename [linecount ] = line_name
160+
161+ # assign x values
150162 for i in range (mujoco .mjMAXLINEPNT ):
151- self .fig .linedata [self ._line_counter ][2 * i ] = float (i )
152- self .fig .linepnt [self ._line_counter ] = 0
153- self ._line_counter += 1
163+ fig .linedata [linecount ][2 * i ] = - float (i )
164+
165+ def add_data_to_line (self , line_name , line_data , fig_idx = 0 ):
166+ fig = self .figs [fig_idx ]
154167
155- def update_graph_line (self , line_name , line_data ):
156168 try :
157169 _line_name = line_name .encode ('utf8' )
158- linenames = self . fig .linename .tolist ()
170+ linenames = fig .linename .tolist ()
159171 line_idx = linenames .index (_line_name )
160172 except ValueError :
161173 raise Exception (
162174 "line name is not valid, add it to list before calling update"
163175 )
164176
165- _x = self .fig .linepnt [line_idx ]
166-
167- if _x < mujoco .mjMAXLINEPNT :
168- self .fig .linedata [line_idx ][2 * _x ] = _x
169- self .fig .linedata [line_idx ][2 * _x + 1 ] = float (line_data )
170- self .fig .linepnt [line_idx ] += 1
171- else :
172- for i in range (mujoco .mjMAXLINEPNT - 1 ):
173- self .fig .linedata [line_idx ][2 * i + 1 ] = self .fig .linedata [line_idx ][2 * (i + 1 )+ 1 ]
174- self .fig .linedata [line_idx ][- 1 ] = float (line_data )
175-
176- def update_graph_size (self , size_div_x = None , size_div_y = None ):
177- if size_div_x is None and size_div_y is None :
178- width , height = glfw .get_framebuffer_size (self .window )
179- width_adjustment = width % 3
180- self .graph_viewport .left = int (2 * width / 3 ) + width_adjustment
181- self .graph_viewport .width = int (width / 3 )
182- self .graph_viewport .height = int (height / 3 )
177+ pnt = min (201 , fig .linepnt [line_idx ] + 1 )
178+ # shift data
179+ for i in range (pnt - 1 , 0 , - 1 ):
180+ fig .linedata [line_idx ][2 * i + 1 ] = fig .linedata [line_idx ][2 * i - 1 ]
183181
184- else :
185- assert size_div_x is not None and size_div_y is None , ""
186- width , height = glfw .get_framebuffer_size (self .window )
187- width_adjustment = width % size_div_x
188- self .graph_viewport .left = (
189- int ((size_div_x - 1 ) * width / size_div_x ) + width_adjustment
190- )
191- self .graph_viewport .width = int (width / size_div_x )
192- self .graph_viewport .height = int (height / size_div_x )
182+ # assign new
183+ fig .linepnt [line_idx ] = pnt ;
184+ fig .linedata [line_idx ][1 ] = line_data ;
193185
194186 def add_marker (self , ** marker_params ):
195187 self ._markers .append (marker_params )
@@ -400,8 +392,18 @@ def update():
400392 self ._create_overlay ()
401393
402394 render_start = time .time ()
403- self .viewport .width , self .viewport .height = glfw .get_framebuffer_size (
404- self .window )
395+
396+ width , height = glfw .get_framebuffer_size (self .window )
397+ self .viewport .width , self .viewport .height = width , height
398+
399+ fig_w , fig_h = int (width / 4 ), int (height / 4 )
400+ width_adjustment = width % 4
401+ for idx in range (len (self .figs_viewport )):
402+ x = int (3 * width / 4 ) + width_adjustment
403+ y = idx * fig_h
404+ vp = mujoco .MjrRect (x , y , fig_w , fig_h )
405+ self .figs_viewport [idx ] = vp
406+
405407 with self ._gui_lock :
406408 # update scene
407409 mujoco .mjv_updateScene (
@@ -434,10 +436,10 @@ def update():
434436
435437 # Handle graph and pausing interactions
436438 if not self ._hide_graph :
437- mujoco . mjr_figure (
438- self . graph_viewport , self . fig , self . ctx
439- )
440- self . update_graph_size ( )
439+ for fig , viewport in zip ( self . figs , self . figs_viewport ):
440+ has_lines = len ([ i for i in fig . linename if i != b'' ])
441+ if has_lines :
442+ mujoco . mjr_figure ( viewport , fig , self . ctx )
441443
442444 glfw .swap_buffers (self .window )
443445 glfw .poll_events ()
0 commit comments