class AnnotationBbox(martist.Artist, _AnnotationBase): """ Annotation-like class, but with offsetbox instead of Text. """ zorder = 3 def __str__(self): return "AnnotationBbox(%g,%g)" % (self.xy[0], self.xy[1]) @docstring.dedent_interpd def __init__( self, offsetbox, xy, xybox=None, xycoords='data', boxcoords=None, frameon=True, pad=0.4, # BboxPatch annotation_clip=None, box_alignment=(0.5, 0.5), bboxprops=None, arrowprops=None, fontsize=None, **kwargs): """ *offsetbox* : OffsetBox instance *xycoords* : same as Annotation but can be a tuple of two strings which are interpreted as x and y coordinates. *boxcoords* : similar to textcoords as Annotation but can be a tuple of two strings which are interpreted as x and y coordinates. *box_alignment* : a tuple of two floats for a vertical and horizontal alignment of the offset box w.r.t. the *boxcoords*. The lower-left corner is (0.0) and upper-right corner is (1.1). other parameters are identical to that of Annotation. """ self.offsetbox = offsetbox self.arrowprops = arrowprops self.set_fontsize(fontsize) if arrowprops is not None: self._arrow_relpos = self.arrowprops.pop("relpos", (0.5, 0.5)) self.arrow_patch = FancyArrowPatch((0, 0), (1, 1), **self.arrowprops) else: self._arrow_relpos = None self.arrow_patch = None _AnnotationBase.__init__(self, xy, xytext=xybox, xycoords=xycoords, textcoords=boxcoords, annotation_clip=annotation_clip) martist.Artist.__init__(self, **kwargs) #self._fw, self._fh = 0., 0. # for alignment self._box_alignment = box_alignment # frame self.patch = FancyBboxPatch( xy=(0.0, 0.0), width=1., height=1., facecolor='w', edgecolor='k', mutation_scale=self.prop.get_size_in_points(), snap=True) self.patch.set_boxstyle("square", pad=pad) if bboxprops: self.patch.set(**bboxprops) self._drawFrame = frameon def contains(self, event): t, tinfo = self.offsetbox.contains(event) #if self.arrow_patch is not None: # a,ainfo=self.arrow_patch.contains(event) # t = t or a # self.arrow_patch is currently not checked as this can be a line - JJ return t, tinfo def get_children(self): children = [self.offsetbox, self.patch] if self.arrow_patch: children.append(self.arrow_patch) return children def set_figure(self, fig): if self.arrow_patch is not None: self.arrow_patch.set_figure(fig) self.offsetbox.set_figure(fig) martist.Artist.set_figure(self, fig) def set_fontsize(self, s=None): """ set fontsize in points """ if s is None: s = rcParams["legend.fontsize"] self.prop = FontProperties(size=s) def get_fontsize(self, s=None): """ return fontsize in points """ return self.prop.get_size_in_points() def update_positions(self, renderer): "Update the pixel positions of the annotated point and the text." xy_pixel = self._get_position_xy(renderer) self._update_position_xybox(renderer, xy_pixel) mutation_scale = renderer.points_to_pixels(self.get_fontsize()) self.patch.set_mutation_scale(mutation_scale) if self.arrow_patch: self.arrow_patch.set_mutation_scale(mutation_scale) def _update_position_xybox(self, renderer, xy_pixel): "Update the pixel positions of the annotation text and the arrow patch." x, y = self.xytext if isinstance(self.textcoords, tuple): xcoord, ycoord = self.textcoords x1, y1 = self._get_xy(renderer, x, y, xcoord) x2, y2 = self._get_xy(renderer, x, y, ycoord) ox0, oy0 = x1, y2 else: ox0, oy0 = self._get_xy(renderer, x, y, self.textcoords) w, h, xd, yd = self.offsetbox.get_extent(renderer) _fw, _fh = self._box_alignment self.offsetbox.set_offset((ox0 - _fw * w + xd, oy0 - _fh * h + yd)) # update patch position bbox = self.offsetbox.get_window_extent(renderer) #self.offsetbox.set_offset((ox0-_fw*w, oy0-_fh*h)) self.patch.set_bounds(bbox.x0, bbox.y0, bbox.width, bbox.height) x, y = xy_pixel ox1, oy1 = x, y if self.arrowprops: x0, y0 = x, y d = self.arrowprops.copy() # Use FancyArrowPatch if self.arrowprops has "arrowstyle" key. # adjust the starting point of the arrow relative to # the textbox. # TODO : Rotation needs to be accounted. relpos = self._arrow_relpos ox0 = bbox.x0 + bbox.width * relpos[0] oy0 = bbox.y0 + bbox.height * relpos[1] # The arrow will be drawn from (ox0, oy0) to (ox1, # oy1). It will be first clipped by patchA and patchB. # Then it will be shrinked by shirnkA and shrinkB # (in points). If patch A is not set, self.bbox_patch # is used. self.arrow_patch.set_positions((ox0, oy0), (ox1, oy1)) fs = self.prop.get_size_in_points() mutation_scale = d.pop("mutation_scale", fs) mutation_scale = renderer.points_to_pixels(mutation_scale) self.arrow_patch.set_mutation_scale(mutation_scale) patchA = d.pop("patchA", self.patch) self.arrow_patch.set_patchA(patchA) def draw(self, renderer): """ Draw the :class:`Annotation` object to the given *renderer*. """ if renderer is not None: self._renderer = renderer if not self.get_visible(): return xy_pixel = self._get_position_xy(renderer) if not self._check_xy(renderer, xy_pixel): return self.update_positions(renderer) if self.arrow_patch is not None: if self.arrow_patch.figure is None and self.figure is not None: self.arrow_patch.figure = self.figure self.arrow_patch.draw(renderer) if self._drawFrame: self.patch.draw(renderer) self.offsetbox.draw(renderer)
class AnnotationBbox(martist.Artist, _AnnotationBase): """ Annotation-like class, but with offsetbox instead of Text. """ zorder = 3 def __str__(self): return "AnnotationBbox(%g,%g)"%(self.xy[0],self.xy[1]) @docstring.dedent_interpd def __init__(self, offsetbox, xy, xybox=None, xycoords='data', boxcoords=None, frameon=True, pad=0.4, # BboxPatch annotation_clip=None, box_alignment=(0.5, 0.5), bboxprops=None, arrowprops=None, fontsize=None, **kwargs): """ *offsetbox* : OffsetBox instance *xycoords* : same as Annotation but can be a tuple of two strings which are interpreted as x and y coordinates. *boxcoords* : similar to textcoords as Annotation but can be a tuple of two strings which are interpreted as x and y coordinates. *box_alignment* : a tuple of two floats for a vertical and horizontal alignment of the offset box w.r.t. the *boxcoords*. The lower-left corner is (0.0) and upper-right corner is (1.1). other parameters are identical to that of Annotation. """ self.offsetbox = offsetbox self.arrowprops = arrowprops self.set_fontsize(fontsize) if arrowprops is not None: self._arrow_relpos = self.arrowprops.pop("relpos", (0.5, 0.5)) self.arrow_patch = FancyArrowPatch((0, 0), (1,1), **self.arrowprops) else: self._arrow_relpos = None self.arrow_patch = None _AnnotationBase.__init__(self, xy, xytext=xybox, xycoords=xycoords, textcoords=boxcoords, annotation_clip=annotation_clip) martist.Artist.__init__(self, **kwargs) #self._fw, self._fh = 0., 0. # for alignment self._box_alignment = box_alignment # frame self.patch = FancyBboxPatch( xy=(0.0, 0.0), width=1., height=1., facecolor='w', edgecolor='k', mutation_scale=self.prop.get_size_in_points(), snap=True ) self.patch.set_boxstyle("square",pad=pad) if bboxprops: self.patch.set(**bboxprops) self._drawFrame = frameon def contains(self,event): t,tinfo = self.offsetbox.contains(event) #if self.arrow_patch is not None: # a,ainfo=self.arrow_patch.contains(event) # t = t or a # self.arrow_patch is currently not checked as this can be a line - JJ return t,tinfo def get_children(self): children = [self.offsetbox, self.patch] if self.arrow_patch: children.append(self.arrow_patch) return children def set_figure(self, fig): if self.arrow_patch is not None: self.arrow_patch.set_figure(fig) self.offsetbox.set_figure(fig) martist.Artist.set_figure(self, fig) def set_fontsize(self, s=None): """ set fontsize in points """ if s is None: s = rcParams["legend.fontsize"] self.prop=FontProperties(size=s) def get_fontsize(self, s=None): """ return fontsize in points """ return self.prop.get_size_in_points() def update_positions(self, renderer): "Update the pixel positions of the annotated point and the text." xy_pixel = self._get_position_xy(renderer) self._update_position_xybox(renderer, xy_pixel) mutation_scale = renderer.points_to_pixels(self.get_fontsize()) self.patch.set_mutation_scale(mutation_scale) if self.arrow_patch: self.arrow_patch.set_mutation_scale(mutation_scale) def _update_position_xybox(self, renderer, xy_pixel): "Update the pixel positions of the annotation text and the arrow patch." x, y = self.xytext if isinstance(self.textcoords, tuple): xcoord, ycoord = self.textcoords x1, y1 = self._get_xy(renderer, x, y, xcoord) x2, y2 = self._get_xy(renderer, x, y, ycoord) ox0, oy0 = x1, y2 else: ox0, oy0 = self._get_xy(renderer, x, y, self.textcoords) w, h, xd, yd = self.offsetbox.get_extent(renderer) _fw, _fh = self._box_alignment self.offsetbox.set_offset((ox0-_fw*w+xd, oy0-_fh*h+yd)) # update patch position bbox = self.offsetbox.get_window_extent(renderer) #self.offsetbox.set_offset((ox0-_fw*w, oy0-_fh*h)) self.patch.set_bounds(bbox.x0, bbox.y0, bbox.width, bbox.height) x, y = xy_pixel ox1, oy1 = x, y if self.arrowprops: x0, y0 = x, y d = self.arrowprops.copy() # Use FancyArrowPatch if self.arrowprops has "arrowstyle" key. # adjust the starting point of the arrow relative to # the textbox. # TODO : Rotation needs to be accounted. relpos = self._arrow_relpos ox0 = bbox.x0 + bbox.width * relpos[0] oy0 = bbox.y0 + bbox.height * relpos[1] # The arrow will be drawn from (ox0, oy0) to (ox1, # oy1). It will be first clipped by patchA and patchB. # Then it will be shrinked by shirnkA and shrinkB # (in points). If patch A is not set, self.bbox_patch # is used. self.arrow_patch.set_positions((ox0, oy0), (ox1,oy1)) fs = self.prop.get_size_in_points() mutation_scale = d.pop("mutation_scale", fs) mutation_scale = renderer.points_to_pixels(mutation_scale) self.arrow_patch.set_mutation_scale(mutation_scale) patchA = d.pop("patchA", self.patch) self.arrow_patch.set_patchA(patchA) def draw(self, renderer): """ Draw the :class:`Annotation` object to the given *renderer*. """ if renderer is not None: self._renderer = renderer if not self.get_visible(): return xy_pixel = self._get_position_xy(renderer) if not self._check_xy(renderer, xy_pixel): return self.update_positions(renderer) if self.arrow_patch is not None: if self.arrow_patch.figure is None and self.figure is not None: self.arrow_patch.figure = self.figure self.arrow_patch.draw(renderer) if self._drawFrame: self.patch.draw(renderer) self.offsetbox.draw(renderer)
class ThreeCompVisualisation: """ Basis to visualise power flow within the hydraulics model as an animation or simulation """ def __init__(self, agent: ThreeCompHydAgent, axis: plt.axis = None, animated: bool = False, detail_annotations: bool = False, basic_annotations: bool = True, black_and_white: bool = False): """ Whole visualisation setup using given agent's parameters :param agent: The agent to be visualised :param axis: If set, the visualisation will be drawn using the provided axis object. Useful for animations or to display multiple models in one plot :param animated: If true the visualisation is set up to deal with frame updates. See animation script for more details. :param detail_annotations: If true, tank distances and sizes are annotated as well. :param basic_annotations: If true, U, LF, and LS are visible :param black_and_white: If true, the visualisation is in black and white """ # matplotlib fontsize rcParams['font.size'] = 10 # plot if no axis was assigned if axis is None: fig = plt.figure(figsize=(8, 5)) self._ax1 = fig.add_subplot(1, 1, 1) else: fig = None self._ax1 = axis if black_and_white: self.__u_color = (0.7, 0.7, 0.7) self.__lf_color = (0.5, 0.5, 0.5) self.__ls_color = (0.3, 0.3, 0.3) self.__ann_color = (0, 0, 0) self.__p_color = (0.5, 0.5, 0.5) elif not black_and_white: self.__u_color = "tab:cyan" self.__lf_color = "tab:orange" self.__ls_color = "tab:red" self.__ann_color = "tab:blue" self.__p_color = "tab:green" # basic parameters for setup self._animated = animated self.__detail_annotations = detail_annotations self._agent = agent self.__offset = 0.2 # U tank with three stripes self.__width_u = 0.3 self._u = None self._u1 = None self._u2 = None self._r1 = None # line marking flow from U to LF self._ann_u = None # U annotation # LF tank self._lf = None self._h = None # fill state self._ann_lf = None # annotation # LS tank self._ls = None self._g = None self._ann_ls = None # annotation LS self._r2 = None # line marking flow from LS to LF # finish the basic layout self.__set_basic_layout() self.update_basic_layout(agent) # now the animation components if self._animated: # U flow self._arr_u_flow = None self._ann_u_flow = None # flow out of tap self._arr_power_flow = None self._ann_power_flow = None # LS flow (R2) self._arr_r2_l_pos = None self._arr_r2_flow = None self._ann_r2_flow = None # time information annotation self._ann_time = None self.__set_animation_layout() self._ax1.add_artist(self._ann_time) # basic annotations are U, LF, and LS if not basic_annotations: self.hide_basic_annotations() # add layout for detailed annotations # detail annotation add greek letters for distances and positions if self.__detail_annotations: self.__set_detailed_annotations_layout() self._ax1.set_xlim(0, 1.05) self._ax1.set_ylim(0, 1.2) else: self._ax1.set_xlim(0, 1.0) self._ax1.set_ylim(0, 1.2) if self.__detail_annotations and self._animated: raise UserWarning("Detailed annotations and animation cannot be combined") self._ax1.set_axis_off() # display plot if no axis object was assigned if fig is not None: plt.subplots_adjust(left=0.02, bottom=0.02, right=0.98, top=0.98) plt.show() plt.close(fig) def __set_detailed_annotations_layout(self): """ Adds components required for a detailed annotations view with denoted positions and distances """ u_width = self.__width_u ls_left = self._ls.get_x() ls_width = self._ls.get_width() lf_left = self._lf.get_x() lf_width = self._lf.get_width() ls_height = self._ls.get_height() # some offset to the bottom offset = self.__offset phi_o = self._agent.phi + offset gamma_o = self._agent.gamma + offset rcParams['text.usetex'] = True self._ann_u_flow = Text(text="$M_{U}$", ha='right', fontsize="xx-large", x=u_width + 0.09, y=phi_o - 0.08) ann_p_ae = Text(text="$p_{U}$", ha='right', fontsize="xx-large", x=u_width + 0.07, y=phi_o + 0.03) self._arr_u_flow = FancyArrowPatch((u_width, phi_o), (u_width + 0.1, phi_o), arrowstyle='-|>', mutation_scale=30, lw=2, color=self.__u_color) self._ax1.annotate('$\phi$', xy=(u_width / 2, phi_o), xytext=(u_width / 2, (phi_o - offset) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$\phi$', xy=(u_width / 2, offset), xytext=(u_width / 2, (phi_o - offset) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ann_power_flow = Text(text="$p$", ha='center', fontsize="xx-large", x=self._ann_lf.get_position()[0], y=offset - 0.06) self._arr_power_flow = FancyArrowPatch((self._ann_lf.get_position()[0], offset - 0.078), (self._ann_lf.get_position()[0], 0.0), arrowstyle='-|>', mutation_scale=30, lw=2, color=self.__p_color) self._ax1.annotate('$h$', xy=(self._ann_lf.get_position()[0] + 0.07, 1 + offset), xytext=(self._ann_lf.get_position()[0] + 0.07, 1 + offset - 0.30), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$h$', xy=(self._ann_lf.get_position()[0] + 0.07, 1 + offset - 0.55), xytext=(self._ann_lf.get_position()[0] + 0.07, 1 + offset - 0.30), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._h.update(dict(xy=(lf_left, offset), width=lf_width, height=1 - 0.55, color=self.__lf_color)) self._ax1.annotate('$g$', xy=(ls_left + ls_width + 0.02, ls_height + gamma_o), xytext=(ls_left + ls_width + 0.02, ls_height * 0.61 + gamma_o), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$g$', xy=(ls_left + ls_width + 0.02, ls_height * 0.3 + gamma_o), xytext=(ls_left + ls_width + 0.02, ls_height * 0.61 + gamma_o), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._g.update(dict(xy=(ls_left, gamma_o), width=ls_width, height=ls_height * 0.3, color=self.__ls_color)) ann_p_an = Text(text="$p_{L}$", ha='left', usetex=True, fontsize="xx-large", x=ls_left - 0.06, y=gamma_o + 0.11) ann_arr_flow = Text(text="$M_{LS}$", ha='left', usetex=True, fontsize="xx-large", x=ls_left - 0.09, y=gamma_o + 0.03) self._ann_r2_flow = Text(text="$M_{LF}$", ha='left', usetex=True, fontsize="xx-large", x=ls_left - 0.04, y=gamma_o - 0.07) self._arr_r2_flow = FancyArrowPatch((ls_left - 0.1, gamma_o), (ls_left, gamma_o), arrowstyle='<|-|>', mutation_scale=30, lw=2, color=self.__ls_color) self._ax1.annotate('$\\theta$', xy=(ls_left + ls_width / 2, 1 + offset), xytext=( ls_left + ls_width / 2, 1 - (1 - (ls_height + gamma_o - offset)) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$\\theta$', xy=(ls_left + ls_width / 2, ls_height + gamma_o), xytext=( ls_left + ls_width / 2, 1 - (1 - (ls_height + gamma_o - offset)) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$\\gamma$', xy=(ls_left + ls_width / 2, offset), xytext=(ls_left + ls_width / 2, (gamma_o - offset) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$\\gamma$', xy=(ls_left + ls_width / 2, gamma_o), xytext=(ls_left + ls_width / 2, (gamma_o - offset) / 2 + offset - 0.015), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$1$', xy=(1.05, 0 + offset), xytext=(1.05, 0.5 + offset), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.annotate('$1$', xy=(1.05, 1 + offset), xytext=(1.05, 0.5 + offset), ha='center', fontsize="xx-large", arrowprops=dict(arrowstyle='-|>', ls='-', fc=self.__ann_color) ) self._ax1.add_artist(ann_arr_flow) # self._ax1.add_artist(ann_p_an) # self._ax1.add_artist(ann_p_ae) self._ax1.axhline(offset, linestyle='--', color=self.__ann_color) self._ax1.axhline(1 + offset - 0.001, linestyle='--', color=self.__ann_color) self._ax1.add_artist(self._ann_power_flow) self._ax1.add_artist(self._arr_power_flow) self._ax1.add_artist(self._arr_u_flow) self._ax1.add_artist(self._ann_u_flow) self._ax1.add_artist(self._arr_r2_flow) self._ax1.add_artist(self._ann_r2_flow) def __set_animation_layout(self): """ Adds layout components that are required for an animation """ offset = self.__offset o_width = self.__width_u phi_o = self._agent.phi + offset gamma_o = self._agent.gamma + offset # U flow (R1) self._arr_u_flow = FancyArrowPatch((o_width, phi_o), (o_width + 0.1, phi_o), arrowstyle='simple', mutation_scale=0, ec='white', fc=self.__u_color) self._ann_u_flow = Text(text="flow: ", ha='right', fontsize="large", x=o_width, y=phi_o - 0.05) # Tap flow (Power) self._arr_power_flow = FancyArrowPatch((self._ann_lf.get_position()[0], offset - 0.05), (self._ann_lf.get_position()[0], 0.0), arrowstyle='simple', mutation_scale=0, ec='white', color=self.__p_color) self._ann_power_flow = Text(text="flow: ", ha='center', fontsize="large", x=self._ann_lf.get_position()[0], y=offset - 0.05) # LS flow (R2) self._arr_r2_l_pos = [(self._ls.get_x(), gamma_o), (self._ls.get_x() - 0.1, gamma_o)] self._arr_r2_flow = FancyArrowPatch(self._arr_r2_l_pos[0], self._arr_r2_l_pos[1], arrowstyle='simple', mutation_scale=0, ec='white', color=self.__ls_color) self._ann_r2_flow = Text(text="flow: ", ha='left', fontsize="large", x=self._ls.get_x(), y=gamma_o - 0.05) # information annotation self._ann_time = Text(x=1, y=0.9 + offset, ha="right") self._ax1.add_artist(self._ann_power_flow) self._ax1.add_artist(self._arr_power_flow) self._ax1.add_artist(self._arr_u_flow) self._ax1.add_artist(self._ann_u_flow) self._ax1.add_artist(self._arr_r2_flow) self._ax1.add_artist(self._ann_r2_flow) def __set_basic_layout(self): """ updates position estimations and layout """ # get sizes from agent lf = self._agent.lf ls = self._agent.ls ls_height = self._agent.height_ls # u_left is 0 u_width = self.__width_u # determine width with size ratio retained lf_left = u_width + 0.1 lf_width = ((lf * ls_height) * (1 - lf_left - 0.1)) / ls ls_left = lf_left + lf_width + 0.1 ls_width = 1 - ls_left # some offset to the bottom offset = self.__offset phi_o = self._agent.phi + offset gamma_o = self._agent.gamma + offset # S tank self._u = Rectangle((0.0, phi_o), 0.05, 1 - self._agent.phi, color=self.__u_color, alpha=0.3) self._u1 = Rectangle((0.05, phi_o), 0.05, 1 - self._agent.phi, color=self.__u_color, alpha=0.6) self._u2 = Rectangle((0.1, phi_o), u_width - 0.1, 1 - self._agent.phi, color=self.__u_color) self._r1 = Line2D([u_width, u_width + 0.1], [phi_o, phi_o], color=self.__u_color) self._ann_u = Text(text="$U$", ha='center', fontsize="xx-large", x=u_width / 2, y=((1 - self._agent.phi) / 2) + phi_o - 0.02) # LF vessel self._lf = Rectangle((lf_left, offset), lf_width, 1, fill=False, ec="black") self._h = Rectangle((lf_left, offset), lf_width, 1, color=self.__lf_color) self._ann_lf = Text(text="$LF$", ha='center', fontsize="xx-large", x=lf_left + (lf_width / 2), y=offset + 0.5 - 0.02) # LS vessel self._ls = Rectangle((ls_left, gamma_o), ls_width, ls_height, fill=False, ec="black") self._g = Rectangle((ls_left, gamma_o), ls_width, ls_height, color=self.__ls_color) self._r2 = Line2D([ls_left, ls_left - 0.1], [gamma_o, gamma_o], color=self.__ls_color) self._ann_ls = Text(text="$LS$", ha='center', fontsize="xx-large", x=ls_left + (ls_width / 2), y=gamma_o + (ls_height / 2) - 0.02) # the basic layout self._ax1.add_line(self._r1) self._ax1.add_line(self._r2) self._ax1.add_artist(self._u) self._ax1.add_artist(self._u1) self._ax1.add_artist(self._u2) self._ax1.add_artist(self._lf) self._ax1.add_artist(self._ls) self._ax1.add_artist(self._h) self._ax1.add_artist(self._g) self._ax1.add_artist(self._ann_u) self._ax1.add_artist(self._ann_lf) self._ax1.add_artist(self._ann_ls) def update_basic_layout(self, agent): """ updates tank positions and sizes according to new agent :param agent: agent to be visualised """ self._agent = agent # get sizes from agent lf = agent.lf ls = agent.ls ls_heigh = agent.height_ls # o_left is 0 u_width = self.__width_u # determine width with size ratio retained lf_left = u_width + 0.1 lf_width = ((lf * ls_heigh) * (1 - lf_left - 0.1)) / (ls + lf * ls_heigh) ls_left = lf_left + lf_width + 0.1 ls_width = 1 - ls_left # some offset to the bottom offset = self.__offset phi_o = agent.phi + offset gamma_o = agent.gamma + offset # S tank self._u.set_bounds(0.0, phi_o, 0.05, 1 - self._agent.phi) self._u1.set_bounds(0.05, phi_o, 0.05, 1 - self._agent.phi) self._u2.set_bounds(0.1, phi_o, u_width - 0.1, 1 - self._agent.phi) self._r1.set_xdata([u_width, u_width + 0.1]) self._r1.set_ydata([phi_o, phi_o]) self._ann_u.set_position(xy=(u_width / 2, ((1 - self._agent.phi) / 2) + phi_o - 0.02)) # LF vessel self._lf.set_bounds(lf_left, offset, lf_width, 1) self._h.set_bounds(lf_left, offset, lf_width, 1) self._ann_lf.set_position(xy=(lf_left + (lf_width / 2), offset + 0.5 - 0.02)) # LS vessel self._ls.set_bounds(ls_left, gamma_o, ls_width, ls_heigh) self._g.set_bounds(ls_left, gamma_o, ls_width, ls_heigh) self._r2.set_xdata([ls_left, ls_left - 0.1]) self._r2.set_ydata([gamma_o, gamma_o]) self._ann_ls.set_position(xy=(ls_left + (ls_width / 2), gamma_o + (ls_heigh / 2) - 0.02)) # update levels self._h.set_height(1 - self._agent.get_h()) self._g.set_height(self._agent.height_ls - self._agent.get_g()) def hide_basic_annotations(self): """ Simply hides the S, LF, and LS text """ self._ann_u.set_text("") self._ann_lf.set_text("") self._ann_ls.set_text("") def update_animation_data(self, frame_number): """ For animations and simulations. The function to call at each frame. :param frame_number: frame number has to be taken because of parent class method :return: an iterable of artists """ if not self._animated: raise UserWarning("Animation flag has to be enabled in order to use this function") # perform one step cur_time = self._agent.get_time() power = self._agent.perform_one_step() # draw some information self._ann_time.set_text("agent \n time: {}".format(int(cur_time))) # power arrow self._ann_power_flow.set_text("power: {}".format(round(power))) self._arr_power_flow.set_mutation_scale(math.log(power + 1) * 10) # oxygen arrow p_u = round(self._agent.get_p_u() * self._agent.hz, 1) max_str = "(MAX)" if p_u == self._agent.m_u else "" self._ann_u_flow.set_text("flow: {} {}".format(p_u, max_str)) self._arr_u_flow.set_mutation_scale(math.log(p_u + 1) * 10) # lactate arrow p_g = round(self._agent.get_p_l() * self._agent.hz, 1) if p_g < 0: max_str = "(MAX)" if p_g == self._agent.m_lf else "" self._arr_r2_flow.set_positions(self._arr_r2_l_pos[1], self._arr_r2_l_pos[0]) else: max_str = "(MAX)" if p_g == self._agent.m_ls else "" self._arr_r2_flow.set_positions(self._arr_r2_l_pos[0], self._arr_r2_l_pos[1]) self._ann_r2_flow.set_text("flow: {} {}".format(p_g, max_str)) self._arr_r2_flow.set_mutation_scale(math.log(abs(p_g) + 1) * 10) # update levels self._h.set_height(1 - self._agent.get_h()) self._g.set_height(self._agent.height_ls - self._agent.get_g()) # list of artists to be drawn return [self._ann_time, self._ann_power_flow, self._arr_power_flow, self._g, self._h]